aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ssl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ssl')
-rw-r--r--lib/ssl/c_src/Makefile.in22
-rw-r--r--lib/ssl/doc/src/Makefile13
-rw-r--r--lib/ssl/doc/src/create_certs.xml14
-rw-r--r--lib/ssl/doc/src/new_ssl.xml53
-rw-r--r--lib/ssl/doc/src/notes.xml124
-rw-r--r--lib/ssl/doc/src/pkix_certs.xml213
-rw-r--r--lib/ssl/doc/src/remember.xml83
-rw-r--r--lib/ssl/doc/src/ssl.xml35
-rw-r--r--lib/ssl/src/ssl.appup.src9
-rw-r--r--lib/ssl/src/ssl.erl95
-rw-r--r--lib/ssl/src/ssl_broker.erl16
-rw-r--r--lib/ssl/src/ssl_certificate.erl53
-rw-r--r--lib/ssl/src/ssl_certificate_db.erl53
-rw-r--r--lib/ssl/src/ssl_connection.erl932
-rw-r--r--lib/ssl/src/ssl_handshake.erl231
-rw-r--r--lib/ssl/src/ssl_handshake.hrl13
-rw-r--r--lib/ssl/src/ssl_internal.hrl14
-rw-r--r--lib/ssl/src/ssl_manager.erl37
-rw-r--r--lib/ssl/src/ssl_record.erl64
-rw-r--r--lib/ssl/src/ssl_record.hrl17
-rw-r--r--lib/ssl/src/ssl_ssl3.erl18
-rw-r--r--lib/ssl/src/ssl_tls1.erl20
-rw-r--r--lib/ssl/test/Makefile137
-rw-r--r--lib/ssl/test/make_certs.erl288
-rw-r--r--lib/ssl/test/old_ssl_active_SUITE.erl387
-rw-r--r--lib/ssl/test/old_ssl_active_once_SUITE.erl409
-rw-r--r--lib/ssl/test/old_ssl_dist_SUITE.erl595
-rw-r--r--lib/ssl/test/old_ssl_misc_SUITE.erl105
-rw-r--r--lib/ssl/test/old_ssl_passive_SUITE.erl374
-rw-r--r--lib/ssl/test/old_ssl_peer_cert_SUITE.erl180
-rw-r--r--lib/ssl/test/old_ssl_protocol_SUITE.erl169
-rw-r--r--lib/ssl/test/old_ssl_verify_SUITE.erl141
-rw-r--r--lib/ssl/test/old_transport_accept_SUITE.erl238
-rw-r--r--lib/ssl/test/ssl.cover7
-rw-r--r--lib/ssl/test/ssl.spec1
-rw-r--r--lib/ssl/test/ssl_basic_SUITE.erl2538
-rw-r--r--lib/ssl/test/ssl_basic_SUITE_data/RANDbin0 -> 512 bytes
-rw-r--r--lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem5
-rw-r--r--lib/ssl/test/ssl_packet_SUITE.erl1746
-rw-r--r--lib/ssl/test/ssl_payload_SUITE.erl726
-rw-r--r--lib/ssl/test/ssl_test_MACHINE.erl935
-rw-r--r--lib/ssl/test/ssl_test_MACHINE.hrl39
-rw-r--r--lib/ssl/test/ssl_test_lib.erl496
-rw-r--r--lib/ssl/test/ssl_to_openssl_SUITE.erl1070
-rw-r--r--lib/ssl/vsn.mk35
45 files changed, 11838 insertions, 912 deletions
diff --git a/lib/ssl/c_src/Makefile.in b/lib/ssl/c_src/Makefile.in
index bd1b2f9375..49a209f2eb 100644
--- a/lib/ssl/c_src/Makefile.in
+++ b/lib/ssl/c_src/Makefile.in
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# 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.
-#
+#
# %CopyrightEnd%
#
@@ -106,6 +106,7 @@ SSL_MAKEFILE =
endif
CC_R_FLAG=@CFLAG_RUNTIME_LIBRARY_PATH@
+
ifeq ($(findstring @,$(CC_R_FLAG)),@)
# Old erts configure used which hasn't replaced @CFLAG_RUNTIME_LIBRARY_PATH@;
# we try our best here instead...
@@ -127,6 +128,12 @@ else
CC_R_OPT = $(CC_R_FLAG)$(SSL_LIBDIR)
endif
+SSL_CC_RUNTIME_LIBRARY_PATH=@SSL_CC_RUNTIME_LIBRARY_PATH@
+# Sigh...
+ifeq ($(findstring @,$(SSL_CC_RUNTIME_LIBRARY_PATH)),@)
+SSL_CC_RUNTIME_LIBRARY_PATH = $(CC_R_OPT)
+endif
+
SSL_LINK_LIB=-L$(SSL_LIBDIR) -lssl -lcrypto
else
# not dynamic crypto lib (default from R11B-5)
@@ -134,6 +141,7 @@ NEED_KERBEROS=@SSL_LINK_WITH_KERBEROS@
NEED_ZLIB=@SSL_LINK_WITH_ZLIB@
SSL_MAKEFILE =
CC_R_OPT =
+SSL_CC_RUNTIME_LIBRARY_PATH=
SSL_LINK_LIB = $(SSL_LIBDIR)/libssl.a $(SSL_LIBDIR)/libcrypto.a
ifeq ($(NEED_KERBEROS),yes)
SSL_LINK_LIB += @STATIC_KERBEROS_LIBS@
@@ -163,11 +171,11 @@ $(OBJDIR)/%$(obj): %.c
# Unix
$(BINDIR)/ssl_esock: $(OBJS)
- $(CC) $(CC_R_OPT) $(PLAIN_CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SSL_LINK_LIB)
+ $(CC) $(PLAIN_CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS) $(SSL_CC_RUNTIME_LIBRARY_PATH) $(SSL_LINK_LIB)
# Win32/Cygwin
$(BINDIR)/ssl_esock.exe: $(OBJS)
- $(LD) -L$(SSL_LIBDIR) -o $@ $^ -lwsock32 -llibeay32 -lssleay32
+ $(LD) $(SSL_CC_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -o $@ $^ -lwsock32 -llibeay32 -lssleay32
# Unix only, and only when linking statically
$(SSL_MAKEFILE):
diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile
index 54d274ef83..fa263d28ab 100644
--- a/lib/ssl/doc/src/Makefile
+++ b/lib/ssl/doc/src/Makefile
@@ -1,19 +1,19 @@
#
# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
-#
+#
+# Copyright Ericsson AB 1999-2010. All Rights Reserved.
+#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# 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.
-#
+#
# %CopyrightEnd%
#
@@ -52,6 +52,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)
+
GIF_FILES = warning.gif
PS_FILES =
diff --git a/lib/ssl/doc/src/create_certs.xml b/lib/ssl/doc/src/create_certs.xml
index 15958ee457..79cc8a0537 100644
--- a/lib/ssl/doc/src/create_certs.xml
+++ b/lib/ssl/doc/src/create_certs.xml
@@ -98,12 +98,12 @@
<title>Creating the Erlang root CA</title>
<p>The Erlang root CA is created with the command</p>
<code type="none">
-\011openssl req -new -x509 -config /some/path/req.cnf \\
-\011 -keyout /some/path/key.pem -out /some/path/cert.pem </code>
+ openssl req -new -x509 -config /some/path/req.cnf \\
+ -keyout /some/path/key.pem -out /some/path/cert.pem </code>
<p>where the option <c>-new</c> indicates that we want to create
a new certificate request and the option <c>-x509</c> implies
that a self-signed certificate is created.
- </p>
+ </p>
</section>
<section>
@@ -111,12 +111,12 @@
<p>The OTP CA is created by first creating a certificate request
with the command</p>
<code type="none">
-\011openssl req -new -config /some/path/req.cnf \\
-\011 -keyout /some/path/key.pem -out /some/path/req.pem </code>
+ openssl req -new -config /some/path/req.cnf \\
+ -keyout /some/path/key.pem -out /some/path/req.pem </code>
<p>and the ask the Erlang CA to sign it:</p>
<code type="none">
-\011openssl ca -batch -notext -config /some/path/req.cnf \\
-\011 -extensions ca_cert -in /some/path/req.pem -out /some/path/cert.pem </code>
+ openssl ca -batch -notext -config /some/path/req.cnf \\
+ -extensions ca_cert -in /some/path/req.pem -out /some/path/cert.pem </code>
<p>where the option <c>-extensions</c> refers to a section in the
configuration file saying that it should create a CA certificate,
and not a plain user certificate.
diff --git a/lib/ssl/doc/src/new_ssl.xml b/lib/ssl/doc/src/new_ssl.xml
index f50f714fe6..08868a1b3c 100644
--- a/lib/ssl/doc/src/new_ssl.xml
+++ b/lib/ssl/doc/src/new_ssl.xml
@@ -60,10 +60,9 @@
very crippled as the control of the ssl-socket was deep
down in openssl making it hard if not impossible to
support all inet options, ipv6 and upgrade of a tcp
- connection to a ssl connection. The alfa version has a
+ connection to a ssl connection. This version has a
few limitations that will be removed before the ssl-4.0
- release. Main differences and limitations in the alfa are
- listed below.</p>
+ release. Main differences and limitations are listed below.</p>
<list type="bulleted">
<item>New ssl requires the crypto
@@ -85,8 +84,6 @@
<item>New API functions are
ssl:shutdown/2, ssl:cipher_suites/[0,1] and
ssl:versions/0</item>
- <item>Diffie-Hellman keyexchange is
- not supported yet.</item>
<item>CRL and policy certificate
extensions are not supported yet. </item>
<item>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0 </item>
@@ -119,8 +116,8 @@
{fail_if_no_peer_cert, boolean()}
{depth, integer()} |
{certfile, path()} | {keyfile, path()} | {password, string()} |
- {cacertfile, path()} | {ciphers, ciphers()} | {ssl_imp, ssl_imp()}
- | {reuse_sessions, boolean()} | {reuse_session, fun()}
+ {cacertfile, path()} | {dhfile, path()} | {ciphers, ciphers()} |
+ {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()}
</c></p>
<p><c>transportoption() = {CallbackModule, DataTag, ClosedTag}
@@ -263,6 +260,12 @@ end
CA certificates (trusted certificates used for verifying a peer
certificate). May be omitted if you do not want to verify
the peer.</item>
+
+ <tag>{dhfile, path()}</tag>
+ <item>Path to file containing PEM encoded Diffie Hellman parameters,
+ for the server to use if a cipher suite using Diffie Hellman key exchange
+ is negotiated. If not specified hardcode parameters will be used.
+ </item>
<tag>{ciphers, ciphers()}</tag>
<item>The function <c>ciphers_suites/0</c> can
@@ -437,30 +440,17 @@ end
</func>
<func>
- <name>peercert(Socket) -> </name>
- <name>peercert(Socket, Opts) -> {ok, Cert} | {error, Reason}</name>
+ <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name>
<fsummary>Return the peer certificate.</fsummary>
<type>
<v>Socket = sslsocket()</v>
- <v>Opts = [] | [otp] | [plain] </v>
- <v>Cert = term()</v>
+ <v>Cert = binary()</v>
<v>Subject = term()</v>
</type>
<desc>
- <p><c>peercert(Cert)</c> is equivalent to <c>peercert(Cert, [])</c>.
- </p>
- <p>The form of the returned certificate depends on the
- options.
- </p>
- <p>If the options list is empty the certificate is returned as
- a DER encoded binary.
- </p>
- <p>The option <c>otp</c> or <c>plain</c> implies that the
- certificate will be returned as a parsed ASN.1 structure in the
- form of an Erlang term. For detail see the public_key application.
- Currently only plain is officially supported see the public_key users
- guide.
- </p>
+ <p>The peer certificate is returned as a DER encoded binary.
+ The certificate can be decoded with <c>public_key:pkix_decode_cert/2</c>.
+ </p>
</desc>
</func>
<func>
@@ -505,6 +495,19 @@ end
</func>
<func>
+ <name>renegotiate(Socket) -> ok | {error, Reason}</name>
+ <fsummary> Initiates a new handshake.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ </type>
+ <desc><p>Initiates a new handshake. A notable return value is
+ <c>{error, renegotiation_rejected}</c> indicating that the peer
+ refused to go through with the renegotiation but the connection
+ is still active using the previously negotiated session.</p>
+ </desc>
+ </func>
+
+ <func>
<name>send(Socket, Data) -> ok | {error, Reason}</name>
<fsummary>Write data to a socket.</fsummary>
<type>
diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml
index 14dfe616ba..9d13427677 100644
--- a/lib/ssl/doc/src/notes.xml
+++ b/lib/ssl/doc/src/notes.xml
@@ -4,7 +4,7 @@
<chapter>
<header>
<copyright>
- <year>1999</year><year>2009</year>
+ <year>1999</year><year>2010</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -13,12 +13,12 @@
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>SSL Release Notes</title>
@@ -30,6 +30,124 @@
</header>
<p>This document describes the changes made to the SSL application.
</p>
+<section><title>SSL 3.11</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixes handling of the option fail_if_no_peer_cert and
+ some undocumented options. Thanks to Rory Byrne.</p>
+ <p>
+ Own Id: OTP-8557</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Support for Diffie-Hellman. ssl-3.11 requires
+ public_key-0.6.</p>
+ <p>
+ Own Id: OTP-7046</p>
+ </item>
+ <item>
+ <p>
+ New ssl now properly handles ssl renegotiation, and
+ initiates a renegotiation if ssl/ltls-sequence numbers
+ comes close to the max value. However RFC-5746 is not yet
+ supported, but will be in an upcoming release.</p>
+ <p>
+ Own Id: OTP-8517</p>
+ </item>
+ <item>
+ <p>
+ When gen_tcp is configured with the {packet,http} option,
+ it automatically switches to expect HTTP Headers after a
+ HTTP Request/Response line has been received. This update
+ fixes ssl to behave in the same way. Thanks to Rory
+ Byrne.</p>
+ <p>
+ Own Id: OTP-8545</p>
+ </item>
+ <item>
+ <p>
+ Ssl now correctly verifies the extended_key_usage
+ extension and also allows the user to verify application
+ specific extensions by supplying an appropriate fun.</p>
+ <p>
+ Own Id: OTP-8554 Aux Id: OTP-8553 </p>
+ </item>
+ <item>
+ <p>
+ Fixed ssl:transport_accept/2 to return properly when
+ socket is closed. Thanks to Rory Byrne.</p>
+ <p>
+ Own Id: OTP-8560</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 3.10.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Fixed a crash in the certificate certification part.</p>
+ <p>
+ Own Id: OTP-8510 Aux Id: seq11525 </p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+<section><title>SSL 3.10.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p><c>ssl:send/2</c> ignored packet option, fix provided
+ by YAMASHINA Hio.</p>
+ <p>Fixed a file cache bug which caused problems when the
+ same file was used for both cert and cacert.</p>
+ <p>Allow <c>ssl:listen/2</c> to be called with option
+ {ssl_imp, old}.</p>
+ <p> Fixed ssl:setopts(Socket, binary) which didn't work
+ for 'new' ssl.</p>.
+ <p>
+ Own Id: OTP-8441</p>
+ </item>
+ <item>
+ <p>
+ Do a controlled shutdown if a non ssl packet arrives as
+ the first packet.</p>
+ <p>
+ Own Id: OTP-8459 Aux Id: seq11505 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>Fixed session reuse (in new_ssl), thanks Wil Tan.</p>
+ <p>Send CA list during Certificate Request (in new_ssl) ,
+ thanks Wil Tan.</p> <p><c>NOTE</c>: SSL (new_ssl)
+ requires public_key-0.5.</p>
+ <p>
+ Own Id: OTP-8372</p>
+ </item>
+ </list>
+ </section>
+
+</section>
<section><title>SSL 3.10.7</title>
diff --git a/lib/ssl/doc/src/pkix_certs.xml b/lib/ssl/doc/src/pkix_certs.xml
index 47818c1b7d..1de807cadc 100644
--- a/lib/ssl/doc/src/pkix_certs.xml
+++ b/lib/ssl/doc/src/pkix_certs.xml
@@ -34,219 +34,24 @@
<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>
<p>Working certificate definitions for the Internet Community are found
- in the the PKIX RFCs <cite id="rfc3279"></cite>and <cite id="rfc3280"></cite>.
+ 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>
<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>
+ </p>
</section>
<section>
<title>PKIX Certificates</title>
- <p>Here we base the PKIX certificate definitions in RFCs <cite id="rfc3279"></cite>and <cite id="rfc3280"></cite>. We however present the
- definitions according to <c>SSL-PKIX.asn1</c> module,
- which is an amelioration of the <c>PKIX1Explicit88.asn1</c>,
- <c>PKIX1Implicit88.asn1</c>, and <c>PKIX1Algorithms88.asn1</c>
- modules. You find all these modules in the <c>pkix</c> subdirectory
- of SSL.
- </p>
- <p>The Erlang terms that are returned by the functions
- <c>ssl:peercert/1/2</c>, <c>ssl_pkix:decode_cert/1/2</c>, and
- <c>ssl_pkix:decode_cert_file/1/2</c> when the option <c>ssl</c>
- is used in those functions, correspond the ASN.1 structures
- described in the sequel.
- </p>
-
- <section>
- <title>Certificate and TBSCertificate</title>
- <code type="none">
-Certificate ::= SEQUENCE {
- tbsCertificate TBSCertificate,
- signatureAlgorithm SignatureAlgorithm,
- signature BIT STRING }
-
-TBSCertificate ::= SEQUENCE {
- version [0] Version DEFAULT v1,
- serialNumber CertificateSerialNumber,
- signature SignatureAlgorithm,
- issuer Name,
- validity Validity,
- subject Name,
- subjectPublicKeyInfo SubjectPublicKeyInfo,
- issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version MUST be v2 or v3
- subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL,
- -- If present, version MUST be v2 or v3
- extensions [3] Extensions OPTIONAL
- -- If present, version MUST be v3 -- }
-
-Version ::= INTEGER { v1(0), v2(1), v3(2) }
-
-CertificateSerialNumber ::= INTEGER
-
-Validity ::= SEQUENCE {
- notBefore Time,
- notAfter Time }
-
-Time ::= CHOICE {
- utcTime UTCTime,
- generalTime GeneralizedTime }
- </code>
- <p>The meaning of the fields <c>version</c>, <c>serialNumber</c>,
- and <c>validity</c> are quite obvious given the type definitions
- above, so we do not go further into their details.
- </p>
- <p>The <c>signatureAlgorithm</c> field of <c>Certificate</c> and
- the <c>signature</c> field of <c>TBSCertificate</c> contain
- the name and parameters of the algorithm used for signing the
- certificate. The values of these two fields must be equal.
- </p>
- <p>The <c>signature</c> field of <c>Certificate</c> contains the
- value of the signature that the issuer computed by using the
- prescribed algorithm.
- </p>
- <p>The <c><![CDATA[issuer<c> and <c>subject]]></c> fields can contain many
- different types av data, and is therefore considered in a
- separate section. The same holds for the <c>extensions</c>
- field.
- The <c>issuerUniqueID</c> and the <c>subjectUniqueID</c> fields
- are not considered further.</p>
- </section>
-
- <section>
- <title>TBSCertificate issuer and subject</title>
- <p></p>
- <code type="none"><![CDATA[
-Name ::= CHOICE { -- only one possibility for now --
- rdnSequence RDNSequence }
-
-RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
-
-DistinguishedName ::= RDNSequence
-
-RelativeDistinguishedName ::=
- SET SIZE (1 .. MAX) OF AttributeTypeAndValue
-
-AttributeTypeAndValue ::= SEQUENCE {
- type ATTRIBUTE-TYPE-AND-VALUE-CLASS.&id
-\011\011({SupportedAttributeTypeAndValues}),
- value ATTRIBUTE-TYPE-AND-VALUE-CLASS.&Type
-\011\011({SupportedAttributeTypeAndValues}{@type}) }
-
-SupportedAttributeTypeAndValues ATTRIBUTE-TYPE-AND-VALUE-CLASS ::=
-\011{ name | surname | givenName | initials | generationQualifier |
-\011 commonName | localityName | stateOrProvinceName | organizationName |
-\011 organizationalUnitName | title | dnQualifier | countryName |
-\011 serialNumber | pseudonym | domainComponent | emailAddress } ]]></code>
- </section>
-
- <section>
- <title>TBSCertificate extensions</title>
- <p>The <c>extensions</c> field of a <c>TBScertificate</c> is a
- sequence of type <c>Extension</c>, defined as follows,</p>
- <code type="none">
-Extension ::= SEQUENCE {
- extnID OBJECT IDENTIFIER,
- critical BOOLEAN DEFAULT FALSE,
- extnValue ANY } </code>
- <p>Each extension has a unique object identifier. An extension
- with a <c>critical</c> value set to <c>TRUE</c><em>must</em>
- be recognised by the reader of a certificate, or else the
- certificate must be rejected.
- </p>
- <p>Extensions are divided into two groups: standard extensions
- and internet certificate extensions. All extensions listed in
- the table that follows are standard extensions, except for
- <c>authorityInfoAccess</c> and <c>subjectInfoAccess</c>, which
- are internet extensions.
- </p>
- <p>Depending on the object identifier the <c>extnValue</c> is
- parsed into an appropriate welldefined structure.
- </p>
- <p>The following table shows the purpose of each extension, but
- does not specify the structure. To see the structure consult
- the <c>PKIX1Implicit88.asn1</c> module.
- </p>
- <table>
- <row>
- <cell align="left" valign="middle">authorityKeyIdentifier</cell>
- <cell align="left" valign="middle">Used by to identify a certificate signed that has multiple signing keys. </cell>
- </row>
- <row>
- <cell align="left" valign="middle">subjectKeyIdentifier</cell>
- <cell align="left" valign="middle">Used to identify certificates that contain a public key. Must appear i CA certificates.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">keyUsage </cell>
- <cell align="left" valign="middle">Defines the purpose of the certificate. Can be one or several of<c>digitalSignature</c>, <c>nonRepudiation</c>,<c>keyEncipherment</c>, <c>dataEncipherment</c>,<c>keyAgreement</c>, <c>keyCertSign</c>, <c>cRLSign</c>,<c>encipherOnly</c>, <c>decipherOnly</c>.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">privateKeyUsagePeriod </cell>
- <cell align="left" valign="middle">Allows certificate issuer to provide a private key usage period to be short than the certificate usage period.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">certificatePolicies</cell>
- <cell align="left" valign="middle">Contains one or more policy information terms indicating the policies under which the certificate has been issued.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">policyMappings</cell>
- <cell align="left" valign="middle">Used i CA certificates. </cell>
- </row>
- <row>
- <cell align="left" valign="middle">subjectAltName</cell>
- <cell align="left" valign="middle">Allows additional identities to be bound the the subject. </cell>
- </row>
- <row>
- <cell align="left" valign="middle">issuerAltName</cell>
- <cell align="left" valign="middle">Allows additional identities to be bound the the issuer.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">subjectDirectoryAttributes</cell>
- <cell align="left" valign="middle">Conveys identity attributes of the subject.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">basicConstraints</cell>
- <cell align="left" valign="middle">Tells if the certificate holder is a CA or not.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">nameConstraints</cell>
- <cell align="left" valign="middle">Used in CA certificates.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">policyConstraints</cell>
- <cell align="left" valign="middle">Used in CA certificates.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">extKeyUsage</cell>
- <cell align="left" valign="middle">Indicates for which purposed the public key may be used. </cell>
- </row>
- <row>
- <cell align="left" valign="middle">cRLDistributionPoints</cell>
- <cell align="left" valign="middle">Indicates how CRL (Certificate Revokation List) information is obtained.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">inhibitAnyPolicy</cell>
- <cell align="left" valign="middle">Used i CA certificates.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">freshestCRL</cell>
- <cell align="left" valign="middle">For CRLs.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">authorityInfoAccess</cell>
- <cell align="left" valign="middle">How to access CA information of the issuer of the certificate.</cell>
- </row>
- <row>
- <cell align="left" valign="middle">subjectInfoAccess</cell>
- <cell align="left" valign="middle">How to access CA information of the subject of the certificate.</cell>
- </row>
- <tcaption>PKIX Extensions</tcaption>
- </table>
- </section>
+ <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/remember.xml b/lib/ssl/doc/src/remember.xml
deleted file mode 100644
index 799627a33c..0000000000
--- a/lib/ssl/doc/src/remember.xml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="latin1" ?>
-<!DOCTYPE chapter SYSTEM "chapter.dtd">
-
-<chapter>
- <header>
- <copyright>
- <year>2003</year><year>2009</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>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.sgml</file>
- </header>
-
- <section>
- <title>Introduction to Certificates</title>
- <p><em>Outline:</em></p>
- <list type="bulleted">
- <item>SSL/TLS protocol - server must have certificate - -what
- the the server sends to the client - client may verify the
- server - server may ask client for certificate - what the
- client sends to the server - server may then verify the client
- - verification - certificate chains - root certificates -
- public keys - key agreement - purpose of certificate - main
- contents of certificate - contents have increased as time went
- by - common file formats for certificates.
- </item>
- <item>private keys - password protection - key generation - file
- formats.
- </item>
- <item>ssl_pkix and alternate decodings.
- </item>
- <item>Attribute Certificates (not used by SSL).
- </item>
- <item>Certificate requests - certificate authorities - signing of
- certificates - certificate revocation lists.
- </item>
- <item>standards: ASN.1, X.509, X.520, PKIX, PKCS, PEM.
- </item>
- <item>incompatibilities between standards (X.509-1997 vs old) - the
- ASN.1 problem of ANY, BIT STRING and OCTET STRING - the module
- ssl_pkix.
- </item>
- <item>test suites: NIST
- </item>
- <item>Warnings: *creation* of trusted certificate (OpenSSL).
- </item>
- <item>Erlang SSL and certificates
- </item>
- <item>The need for seeding the random generator. See also John
- S. Denker: High-Entropy Symbol Generator
- (http://www.monmouth.com/~jsd).
- </item>
- <item>links to standards and documents. Books (Rescorla).
- </item>
- <item>ASN.1 crash course.
- </item>
- <item>Nagel algorithm.
- </item>
- </list>
- <p>For an introduction to ASN.1 see <url href="http://asn1.elibel.tm.fr/">ASN.1 Information Site</url>.
- </p>
- </section>
-</chapter>
-
-
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 9b780b14ce..217eb791d0 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -347,39 +347,17 @@
</desc>
</func>
<func>
- <name>peercert(Socket) -> </name>
- <name>peercert(Socket, Opts) -> {ok, Cert} | {ok, Subject} | {error, Reason}</name>
+ <name>peercert(Socket) -> {ok, Cert} | {error, Reason}</name>
<fsummary>Return the peer certificate.</fsummary>
<type>
<v>Socket = sslsocket()</v>
- <v>Opts = [pkix | ssl | subject]()</v>
- <v>Cert = term()()</v>
+ <v>Cert = binary()()</v>
<v>Subject = term()()</v>
</type>
<desc>
- <p><c>peercert(Cert)</c> is equivalent to <c>peercert(Cert, [])</c>.
- </p>
- <p>The form of the returned certificate depends on the
- options.
- </p>
- <p>If the options list is empty the certificate is returned as
- a DER encoded binary.
- </p>
- <p>The options <c>pkix</c> and <c>ssl</c> implies that the
- certificate is returned as a parsed ASN.1 structure in the
- form of an Erlang term.
- </p>
- <p>The <c>ssl</c> option gives a more elaborate return
- structure, with more explicit information. In particular
- object identifiers are replaced by atoms.
- </p>
- <p>The options <c>pkix</c>, and <c>ssl</c> are mutually
- exclusive.
- </p>
- <p>The option <c>subject</c> implies that only the subject's
- distinguished name part of the peer certificate is returned.
- It can only be used together with the option <c>pkix</c> or
- the option <c>ssl</c>.</p>
+ <p>Returns the DER encoded peer certificate, the certificate can be decoded with
+ <c>public_key:pkix_decode_cert/2</c>.
+ </p>
</desc>
</func>
<func>
@@ -719,8 +697,7 @@
<section>
<title>SEE ALSO</title>
- <p>gen_tcp(3), inet(3)
- </p>
+ <p>gen_tcp(3), inet(3) public_key(3) </p>
</section>
</erlref>
diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src
index 755c3b51b5..e8ae6846aa 100644
--- a/lib/ssl/src/ssl.appup.src
+++ b/lib/ssl/src/ssl.appup.src
@@ -7,7 +7,10 @@
{"3.10.3", [{restart_application, ssl}]},
{"3.10.4", [{restart_application, ssl}]},
{"3.10.5", [{restart_application, ssl}]},
- {"3.10.6", [{restart_application, ssl}]}
+ {"3.10.6", [{restart_application, ssl}]},
+ {"3.10.7", [{restart_application, ssl}]},
+ {"3.10.8", [{restart_application, ssl}]},
+ {"3.10.9", [{restart_application, ssl}]}
],
[
{"3.10", [{restart_application, ssl}]},
@@ -16,6 +19,8 @@
{"3.10.3", [{restart_application, ssl}]},
{"3.10.4", [{restart_application, ssl}]},
{"3.10.5", [{restart_application, ssl}]},
- {"3.10.6", [{restart_application, ssl}]}
+ {"3.10.6", [{restart_application, ssl}]},
+ {"3.10.8", [{restart_application, ssl}]},
+ {"3.10.9", [{restart_application, ssl}]}
]}.
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index 1222fe97fd..3cd4c7fdbd 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -29,13 +29,15 @@
connect/3, connect/2, connect/4, connection_info/1,
controlling_process/2, listen/2, pid/1, peername/1, recv/2, recv/3,
send/2, getopts/2, setopts/2, seed/1, sockname/1, peercert/1,
- peercert/2, version/0, versions/0, session_info/1, format_error/1]).
+ peercert/2, version/0, versions/0, session_info/1, format_error/1,
+ renegotiate/1]).
%% Should be deprecated as soon as old ssl is removed
%%-deprecated({pid, 1, next_major_release}).
-include("ssl_int.hrl").
-include("ssl_internal.hrl").
+-include("ssl_record.hrl").
-record(config, {ssl, %% SSL parameters
inet_user, %% User set inet options
@@ -129,7 +131,8 @@ listen(Port, Options0) ->
%% so that new and old ssl can be run by the same
%% code, however the option will be ignored by old ssl
%% that hardcodes reuseaddr to true in its portprogram.
- Options = proplists:delete(reuseaddr, Options0),
+ Options1 = proplists:delete(reuseaddr, Options0),
+ Options = proplists:delete(ssl_imp, Options1),
old_listen(Port, Options);
Value ->
{error, {eoptions, {ssl_imp, Value}}}
@@ -150,18 +153,23 @@ transport_accept(#sslsocket{pid = {ListenSocket, #config{cb=CbInfo, ssl=SslOpts}
%% and options should be inherited.
EmOptions = emulated_options(),
{ok, InetValues} = inet:getopts(ListenSocket, EmOptions),
- {CbModule,_,_} = CbInfo,
- {ok, Socket} = CbModule:accept(ListenSocket, Timeout),
- inet:setopts(Socket, internal_inet_values()),
- {ok, Port} = inet:port(Socket),
- case ssl_connection_sup:start_child([server, "localhost", Port, Socket,
- {SslOpts, socket_options(InetValues)}, self(),
- CbInfo]) of
- {ok, Pid} ->
- CbModule:controlling_process(Socket, Pid),
- {ok, SslSocket#sslsocket{pid = Pid}};
- {error, Reason} ->
- {error, Reason}
+ ok = inet:setopts(ListenSocket, internal_inet_values()),
+ {CbModule,_,_} = CbInfo,
+ case CbModule:accept(ListenSocket, Timeout) of
+ {ok, Socket} ->
+ ok = inet:setopts(ListenSocket, InetValues),
+ {ok, Port} = inet:port(Socket),
+ ConnArgs = [server, "localhost", Port, Socket,
+ {SslOpts, socket_options(InetValues)}, self(), CbInfo],
+ case ssl_connection_sup:start_child(ConnArgs) of
+ {ok, Pid} ->
+ CbModule:controlling_process(Socket, Pid),
+ {ok, SslSocket#sslsocket{pid = Pid}};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
end;
transport_accept(#sslsocket{} = ListenSocket, Timeout) ->
@@ -366,8 +374,10 @@ getopts(#sslsocket{} = Socket, Options) ->
%%
%% Description:
%%--------------------------------------------------------------------
-setopts(#sslsocket{fd = new_ssl, pid = Pid}, Options) when is_pid(Pid) ->
- ssl_connection:set_opts(Pid, Options);
+setopts(#sslsocket{fd = new_ssl, pid = Pid}, Opts0) when is_pid(Pid) ->
+ Opts = proplists:expand([{binary, [{mode, binary}]},
+ {list, [{mode, list}]}], Opts0),
+ ssl_connection:set_opts(Pid, Opts);
setopts(#sslsocket{fd = new_ssl, pid = {ListenSocket, _}}, OptTags) ->
inet:setopts(ListenSocket, OptTags);
setopts(#sslsocket{} = Socket, Options) ->
@@ -433,6 +443,10 @@ versions() ->
AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS,
[{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}].
+
+renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
+ ssl_connection:renegotiation(Pid).
+
%%%--------------------------------------------------------------
%%% Internal functions
%%%--------------------------------------------------------------------
@@ -506,6 +520,9 @@ handle_options(Opts0, Role) ->
end
end,
+ UserFailIfNoPeerCert = validate_option(fail_if_no_peer_cert,
+ proplists:get_value(fail_if_no_peer_cert, Opts, false)),
+
{Verify, FailIfNoPeerCert, CaCertDefault} =
%% Handle 0, 1, 2 for backwards compatibility
case proplists:get_value(verify, Opts, verify_none) of
@@ -518,9 +535,7 @@ handle_options(Opts0, Role) ->
verify_none ->
{verify_none, false, ca_cert_default(verify_none, Role)};
verify_peer ->
- {verify_peer, proplists:get_value(fail_if_no_peer_cert,
- Opts, false),
- ca_cert_default(verify_peer, Role)};
+ {verify_peer, UserFailIfNoPeerCert, ca_cert_default(verify_peer, Role)};
Value ->
throw({error, {eoptions, {verify, Value}}})
end,
@@ -531,28 +546,31 @@ handle_options(Opts0, Role) ->
versions = handle_option(versions, Opts, []),
verify = validate_option(verify, Verify),
verify_fun = handle_option(verify_fun, Opts, VerifyFun),
- fail_if_no_peer_cert = validate_option(fail_if_no_peer_cert,
- FailIfNoPeerCert),
+ fail_if_no_peer_cert = FailIfNoPeerCert,
verify_client_once = handle_option(verify_client_once, Opts, false),
+ validate_extensions_fun = handle_option(validate_extensions_fun, Opts, undefined),
depth = handle_option(depth, Opts, 1),
certfile = CertFile,
keyfile = handle_option(keyfile, Opts, CertFile),
key = handle_option(key, Opts, undefined),
password = handle_option(password, Opts, ""),
cacertfile = handle_option(cacertfile, Opts, CaCertDefault),
+ dhfile = handle_option(dhfile, Opts, undefined),
ciphers = handle_option(ciphers, Opts, []),
%% Server side option
reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun),
reuse_sessions = handle_option(reuse_sessions, Opts, true),
+ renegotiate_at = handle_option(renegotiate_at, Opts, ?DEFAULT_RENEGOTIATE_AT),
debug = handle_option(debug, Opts, [])
},
CbInfo = proplists:get_value(cb_info, Opts, {gen_tcp, tcp, tcp_closed}),
- SslOptions = [versions, verify, verify_fun,
+ SslOptions = [versions, verify, verify_fun, validate_extensions_fun,
+ fail_if_no_peer_cert, verify_client_once,
depth, certfile, keyfile,
- key, password, cacertfile, ciphers,
+ key, password, cacertfile, dhfile, ciphers,
debug, reuse_session, reuse_sessions, ssl_imp,
- cd_info],
+ cb_info, renegotiate_at],
SockOpts = lists:foldl(fun(Key, PropList) ->
proplists:delete(Key, PropList)
@@ -582,6 +600,9 @@ validate_option(fail_if_no_peer_cert, Value)
validate_option(verify_client_once, Value)
when Value == true; Value == false ->
Value;
+
+validate_option(validate_extensions_fun, Value) when Value == undefined; is_function(Value) ->
+ Value;
validate_option(depth, Value) when is_integer(Value),
Value >= 0, Value =< 255->
Value;
@@ -602,11 +623,17 @@ validate_option(cacertfile, undefined) ->
"";
validate_option(cacertfile, Value) when is_list(Value), Value =/= "" ->
Value;
+validate_option(dhfile, undefined = Value) ->
+ Value;
+validate_option(dhfile, Value) when is_list(Value), Value =/= "" ->
+ Value;
validate_option(ciphers, Value) when is_list(Value) ->
Version = ssl_record:highest_protocol_version([]),
try cipher_suites(Version, Value)
catch
exit:_ ->
+ throw({error, {eoptions, {ciphers, Value}}});
+ error:_->
throw({error, {eoptions, {ciphers, Value}}})
end;
validate_option(reuse_session, Value) when is_function(Value) ->
@@ -614,6 +641,9 @@ validate_option(reuse_session, Value) when is_function(Value) ->
validate_option(reuse_sessions, Value) when Value == true;
Value == false ->
Value;
+validate_option(renegotiate_at, Value) when is_integer(Value) ->
+ min(Value, ?DEFAULT_RENEGOTIATE_AT);
+
validate_option(debug, Value) when is_list(Value); Value == true ->
Value;
validate_option(Opt, Value) ->
@@ -625,7 +655,7 @@ validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1';
Version == tlsv1;
Version == sslv3 ->
validate_versions(Rest, Versions);
-validate_versions(Ver, Versions) ->
+validate_versions([Ver| _], Versions) ->
throw({error, {eoptions, {Ver, {versions, Versions}}}}).
validate_inet_option(mode, Value)
@@ -829,6 +859,11 @@ version() ->
Vsns
end,
{ok, {SSLVsn, CompVsn, LibVsn}}.
+
+min(N,M) when N < M ->
+ N;
+min(_, M) ->
+ M.
%% Only used to remove exit messages from old ssl
%% First is a nonsense clause to provide some
diff --git a/lib/ssl/src/ssl_broker.erl b/lib/ssl/src/ssl_broker.erl
index 178fb5fcb9..7ef88baf2b 100644
--- a/lib/ssl/src/ssl_broker.erl
+++ b/lib/ssl/src/ssl_broker.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -333,9 +333,9 @@ init([Client, Type]) ->
debug1(Debug, Type, "in start, client = ~w", [Client]),
{ok, #st{brokertype = Type, server = Server, client = Client,
collector = Client, debug = Debug}};
- true ->
- {stop, no_ssl_server}
- end.
+ true ->
+ {stop, no_ssl_server}
+ end.
%%
diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl
index d97b61a5ce..686e90a70c 100644
--- a/lib/ssl/src/ssl_certificate.erl
+++ b/lib/ssl/src/ssl_certificate.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -29,10 +29,12 @@
-include("ssl_alert.hrl").
-include("ssl_internal.hrl").
-include("ssl_debug.hrl").
+-include_lib("public_key/include/public_key.hrl").
-export([trusted_cert_and_path/3,
certificate_chain/2,
- file_to_certificats/1]).
+ file_to_certificats/1,
+ validate_extensions/6]).
%%====================================================================
%% Internal application API
@@ -87,6 +89,30 @@ file_to_certificats(File) ->
{ok, List} = ssl_manager:cache_pem_file(File),
[Bin || {cert, Bin, not_encrypted} <- List].
+
+%% Validates ssl/tls specific extensions
+validate_extensions([], ValidationState, UnknownExtensions, _, AccErr, _) ->
+ {UnknownExtensions, ValidationState, AccErr};
+
+validate_extensions([#'Extension'{extnID = ?'id-ce-extKeyUsage',
+ extnValue = KeyUse,
+ critical = true} | Rest],
+ ValidationState, UnknownExtensions, Verify, AccErr0, Role) ->
+ case is_valid_extkey_usage(KeyUse, Role) of
+ true ->
+ validate_extensions(Rest, ValidationState, UnknownExtensions,
+ Verify, AccErr0, Role);
+ false ->
+ AccErr =
+ not_valid_extension({bad_cert, invalid_ext_key_usage}, Verify, AccErr0),
+ validate_extensions(Rest, ValidationState, UnknownExtensions, Verify, AccErr, Role)
+ end;
+
+validate_extensions([Extension | Rest], ValidationState, UnknownExtensions,
+ Verify, AccErr, Role) ->
+ validate_extensions(Rest, ValidationState, [Extension | UnknownExtensions],
+ Verify, AccErr, Role).
+
%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
@@ -154,3 +180,18 @@ not_valid(Alert, true, _) ->
throw(Alert);
not_valid(_, false, {ErlCert, Path}) ->
{ErlCert, Path, [{bad_cert, unknown_ca}]}.
+
+is_valid_extkey_usage(KeyUse, client) ->
+ %% Client wants to verify server
+ is_valid_key_usage(KeyUse,?'id-kp-serverAuth');
+is_valid_extkey_usage(KeyUse, server) ->
+ %% Server wants to verify client
+ is_valid_key_usage(KeyUse, ?'id-kp-clientAuth').
+
+is_valid_key_usage(KeyUse, Use) ->
+ lists:member(Use, KeyUse).
+
+not_valid_extension(Error, true, _) ->
+ throw(Error);
+not_valid_extension(Error, false, AccErrors) ->
+ [Error | AccErrors].
diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl
index decc6c9fea..b8c3c6f6b7 100644
--- a/lib/ssl/src/ssl_certificate_db.erl
+++ b/lib/ssl/src/ssl_certificate_db.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -27,7 +27,7 @@
-export([create/0, remove/1, add_trusted_certs/3,
remove_trusted_certs/2, lookup_trusted_cert/3, issuer_candidate/1,
- cache_pem_file/3]).
+ lookup_cached_certs/1, cache_pem_file/3]).
%%====================================================================
%% Internal application API
@@ -74,6 +74,9 @@ lookup_trusted_cert(Ref, SerialNumber, Issuer) ->
{ok, Certs}
end.
+lookup_cached_certs(File) ->
+ ets:lookup(certificate_db_name(), {file, File}).
+
%%--------------------------------------------------------------------
%% Function: add_trusted_certs(Pid, File, Db) -> {ok, Ref}
%% Pid = pid()
@@ -90,7 +93,7 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
undefined ->
NewRef = make_ref(),
add_certs_from_file(File, NewRef, CertsDb),
- insert(File, NewRef, 1, FileToRefDb),
+ insert(File, NewRef, 1, FileToRefDb),
NewRef;
[OldRef] ->
ref_count(File,FileToRefDb,1),
@@ -104,14 +107,11 @@ add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) ->
%%
%% Description: Cache file as binary in DB
%%--------------------------------------------------------------------
-cache_pem_file(Pid, File, [_CertsDb, FileToRefDb, PidToFileDb]) ->
- try ref_count(File, FileToRefDb,1)
- catch _:_ ->
- {ok, Content} = public_key:pem_to_der(File),
- insert(File,Content,1,FileToRefDb)
- end,
+cache_pem_file(Pid, File, [CertsDb, _FileToRefDb, PidToFileDb]) ->
+ Res = {ok, Content} = public_key:pem_to_der(File),
+ insert({file, File}, Content, CertsDb),
insert(Pid, File, PidToFileDb),
- {ok, FileToRefDb}.
+ Res.
%%--------------------------------------------------------------------
%% Function: remove_trusted_certs(Pid, Db) -> _
@@ -123,15 +123,16 @@ remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) ->
Files = lookup(Pid, PidToFileDb),
delete(Pid, PidToFileDb),
Clear = fun(File) ->
- case ref_count(File, FileToRefDb, -1) of
- 0 ->
- case lookup(File, FileToRefDb) of
- [Ref] when is_reference(Ref) ->
- remove_certs(Ref, CertsDb);
- _ -> ok
- end,
- delete(File, FileToRefDb);
- _ ->
+ delete({file,File}, CertsDb),
+ try
+ 0 = ref_count(File, FileToRefDb, -1),
+ case lookup(File, FileToRefDb) of
+ [Ref] when is_reference(Ref) ->
+ remove_certs(Ref, CertsDb);
+ _ -> ok
+ end,
+ delete(File, FileToRefDb)
+ catch _:_ ->
ok
end
end,
@@ -158,6 +159,8 @@ issuer_candidate(no_candidate) ->
case ets:first(Db) of
'$end_of_table' ->
no_more_candidates;
+ {file, _} = Key ->
+ issuer_candidate(Key);
Key ->
[Cert] = lookup(Key, Db),
{Key, Cert}
@@ -168,6 +171,8 @@ issuer_candidate(PrevCandidateKey) ->
case ets:next(Db, PrevCandidateKey) of
'$end_of_table' ->
no_more_candidates;
+ {file, _} = Key ->
+ issuer_candidate(Key);
Key ->
[Cert] = lookup(Key, Db),
{Key, Cert}
@@ -189,7 +194,7 @@ ref_count(Key, Db,N) ->
ets:update_counter(Db,Key,N).
delete(Key, Db) ->
- true = ets:delete(Db, Key).
+ _ = ets:delete(Db, Key).
lookup(Key, Db) ->
case ets:lookup(Db, Key) of
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 178c055cdf..8ff001b172 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -41,15 +41,14 @@
%% Internal application API
-export([send/2, send/3, recv/3, connect/7, accept/6, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
- peer_certificate/1,
- sockname/1, peername/1]).
+ peer_certificate/1, sockname/1, peername/1, renegotiation/1]).
%% Called by ssl_connection_sup
-export([start_link/7]).
%% gen_fsm callbacks
--export([init/1, hello/2, certify/2, cipher/2, connection/2, connection/3, abbreviated/2,
- handle_event/3,
+-export([init/1, hello/2, certify/2, cipher/2, connection/2,
+ abbreviated/2, handle_event/3,
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
-record(state, {
@@ -78,17 +77,25 @@
client_certificate_requested = false,
key_algorithm, % atom as defined by cipher_suite
public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams}
- private_key, % PKIX: 'RSAPrivateKey'
- diffie_hellman_params, %
+ private_key, % PKIX: #'RSAPrivateKey'{}
+ diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side
+ diffie_hellman_keys, % {PublicKey, PrivateKey}
premaster_secret, %
cert_db_ref, % ets_table()
from, % term(), where to reply
bytes_to_read, % integer(), # bytes to read in passive mode
user_data_buffer, % binary()
%% tls_buffer, % Keeps a lookahead one packet if available
- log_alert % boolan()
+ log_alert, % boolean()
+ renegotiation, % {boolean(), From | internal | peer}
+ recv_during_renegotiation, %boolean()
+ send_queue % queue()
}).
+-define(DEFAULT_DIFFIE_HELLMAN_PARAMS,
+ #'DHParameter'{prime = ?DEFAULT_DIFFIE_HELLMAN_PRIME,
+ base = ?DEFAULT_DIFFIE_HELLMAN_GENERATOR}).
+
%%====================================================================
%% Internal application API
%%====================================================================
@@ -99,15 +106,15 @@
%% Description:
%%--------------------------------------------------------------------
send(Pid, Data) ->
- sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
+ sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, infinity).
send(Pid, Data, Timeout) ->
- sync_send_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
+ sync_send_all_state_event(Pid, {application_data, erlang:iolist_to_binary(Data)}, Timeout).
%%--------------------------------------------------------------------
%% Function:
%%
%% Description:
%%--------------------------------------------------------------------
-recv(Pid, Length, Timeout) -> % TODO: Prio with renegotiate?
+recv(Pid, Length, Timeout) ->
sync_send_all_state_event(Pid, {recv, Length}, Timeout).
%%--------------------------------------------------------------------
%% Function:
@@ -209,6 +216,14 @@ session_info(ConnectionPid) ->
peer_certificate(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, peer_certificate).
+%%--------------------------------------------------------------------
+%% Function:
+%%
+%% Description:
+%%--------------------------------------------------------------------
+renegotiation(ConnectionPid) ->
+ sync_send_all_state_event(ConnectionPid, renegotiate).
+
%%====================================================================
%% ssl_connection_sup API
%%====================================================================
@@ -224,7 +239,6 @@ start_link(Role, Host, Port, Socket, Options, User, CbInfo) ->
gen_fsm:start_link(?MODULE, [Role, Host, Port, Socket, Options,
User, CbInfo], []).
-
%%====================================================================
%% gen_fsm callbacks
%%====================================================================
@@ -243,12 +257,13 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options,
Hashes0 = ssl_handshake:init_hashes(),
try ssl_init(SSLOpts, Role) of
- {ok, Ref, CacheRef, OwnCert, Key} ->
+ {ok, Ref, CacheRef, OwnCert, Key, DHParams} ->
State = State0#state{tls_handshake_hashes = Hashes0,
own_cert = OwnCert,
cert_db_ref = Ref,
session_cache = CacheRef,
- private_key = Key},
+ private_key = Key,
+ diffie_hellman_params = DHParams},
{ok, hello, State}
catch
throw:Error ->
@@ -261,18 +276,20 @@ init([Role, Host, Port, Socket, {SSLOpts, _} = Options,
%% {next_state, NextStateName,
%% NextState, Timeout} |
%% {stop, Reason, NewState}
-%% Description:There should be one instance of this function for each possible
-%% state name. Whenever a gen_fsm receives an event sent using
-%% gen_fsm:send_event/2, the instance of this function with the same name as
-%% the current state name StateName is called to handle the event. It is also
-%% called if a timeout occurs.
+%%
+%% Description:There should be one instance of this function for each
+%% possible state name. Whenever a gen_fsm receives an event sent
+%% using gen_fsm:send_event/2, the instance of this function with the
+%% same name as the current state name StateName is called to handle
+%% the event. It is also called if a timeout occurs.
%%--------------------------------------------------------------------
hello(socket_control, #state{host = Host, port = Port, role = client,
ssl_options = SslOpts,
transport_cb = Transport, socket = Socket,
connection_states = ConnectionStates}
= State0) ->
- Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates, SslOpts),
+ Hello = ssl_handshake:client_hello(Host, Port,
+ ConnectionStates, SslOpts),
Version = Hello#client_hello.client_version,
Hashes0 = ssl_handshake:init_hashes(),
{BinMsg, CS2, Hashes1} =
@@ -289,7 +306,7 @@ hello(socket_control, #state{host = Host, port = Port, role = client,
hello(socket_control, #state{role = server} = State) ->
{next_state, hello, next_record(State)};
-hello(hello, #state{role = client} = State) ->
+hello(#hello_request{}, #state{role = client} = State) ->
{next_state, hello, State};
hello(#server_hello{cipher_suite = CipherSuite,
@@ -301,13 +318,14 @@ hello(#server_hello{cipher_suite = CipherSuite,
host = Host, port = Port,
session_cache = Cache,
session_cache_cb = CacheCb} = State0) ->
+
{Version, NewId, ConnectionStates1} =
ssl_handshake:hello(Hello, ConnectionStates0),
{KeyAlgorithm, _, _, _} =
ssl_cipher:suite_definition(CipherSuite),
- PremasterSecret = make_premaster_secret(ReqVersion),
+ PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm),
State = State0#state{key_algorithm = KeyAlgorithm,
negotiated_version = Version,
@@ -358,47 +376,45 @@ hello(Hello = #client_hello{client_version = ClientVersion},
abbreviated(socket_control, #state{role = server} = State) ->
{next_state, abbreviated, State};
-abbreviated(hello, State) ->
+abbreviated(#hello_request{}, State) ->
{next_state, certify, State};
abbreviated(Finished = #finished{},
#state{role = server,
negotiated_version = Version,
tls_handshake_hashes = Hashes,
- session = #session{master_secret = MasterSecret},
- from = From} = State) ->
+ session = #session{master_secret = MasterSecret}} =
+ State0) ->
case ssl_handshake:verify_connection(Version, Finished, client,
MasterSecret, Hashes) of
verified ->
- gen_fsm:reply(From, connected),
- {next_state, connection, next_record_if_active(State)};
- #alert{} = Alert ->
- handle_own_alert(Alert, Version, abbreviated, State),
- {stop, normal, State}
+ State = ack_connection(State0),
+ next_state_connection(State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, abbreviated, State0),
+ {stop, normal, State0}
end;
abbreviated(Finished = #finished{},
#state{role = client, tls_handshake_hashes = Hashes0,
session = #session{master_secret = MasterSecret},
- from = From,
- negotiated_version = Version} = State) ->
+ negotiated_version = Version} = State0) ->
case ssl_handshake:verify_connection(Version, Finished, server,
MasterSecret, Hashes0) of
verified ->
- {ConnectionStates, Hashes} = finalize_client_handshake(State),
- gen_fsm:reply(From, connected),
- {next_state, connection,
- next_record_if_active(State#state{tls_handshake_hashes = Hashes,
- connection_states =
- ConnectionStates})};
+ {ConnectionStates, Hashes} = finalize_client_handshake(State0),
+ State = ack_connection(State0),
+ next_state_connection(State#state{tls_handshake_hashes = Hashes,
+ connection_states =
+ ConnectionStates});
#alert{} = Alert ->
- handle_own_alert(Alert, Version, abbreviated, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, abbreviated, State0),
+ {stop, normal, State0}
end.
certify(socket_control, #state{role = server} = State) ->
{next_state, certify, State};
-certify(hello, State) ->
+certify(#hello_request{}, State) ->
{next_state, certify, State};
certify(#certificate{asn1_certificates = []},
@@ -415,52 +431,71 @@ certify(#certificate{asn1_certificates = []},
ssl_options = #ssl_options{verify = verify_peer,
fail_if_no_peer_cert = false}} =
State) ->
- {next_state, certify, next_record(State#state{client_certificate_requested = false})};
+ {next_state, certify,
+ next_record(State#state{client_certificate_requested = false})};
certify(#certificate{} = Cert,
- #state{session = Session,
- negotiated_version = Version,
+ #state{negotiated_version = Version,
+ role = Role,
cert_db_ref = CertDbRef,
- ssl_options = Opts} = State0) ->
+ ssl_options = Opts} = State) ->
case ssl_handshake:certify(Cert, CertDbRef, Opts#ssl_options.depth,
Opts#ssl_options.verify,
- Opts#ssl_options.verify_fun) of
+ Opts#ssl_options.verify_fun,
+ Opts#ssl_options.validate_extensions_fun, Role) of
{PeerCert, PublicKeyInfo} ->
- State = State0#state{session =
- Session#session{peer_certificate = PeerCert},
- public_key_info = PublicKeyInfo,
- client_certificate_requested = false
- },
- {next_state, certify, next_record(State)};
+ handle_peer_cert(PeerCert, PublicKeyInfo,
+ State#state{client_certificate_requested = false});
#alert{} = Alert ->
- handle_own_alert(Alert, Version, certify_certificate, State0),
- {stop, normal, State0}
+ handle_own_alert(Alert, Version, certify_certificate, State),
+ {stop, normal, State}
end;
certify(#server_key_exchange{} = KeyExchangeMsg,
- #state{role = client,
- key_algorithm = Alg} = State)
- when Alg == dhe_dss; Alg == dhe_rsa; Alg == dh_anon; Alg == krb5 ->
- NewState = handle_server_key(KeyExchangeMsg, State),
- {next_state, certify, NewState};
+ #state{role = client, negotiated_version = Version,
+ key_algorithm = Alg} = State0)
+ when Alg == dhe_dss; Alg == dhe_rsa ->%%Not imp:Alg == dh_anon;Alg == krb5 ->
+ case handle_server_key(KeyExchangeMsg, State0) of
+ #state{} = State ->
+ {next_state, certify, next_record(State)};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version, certify_server_keyexchange,
+ State0),
+ {stop, normal, State0}
+ end;
certify(#server_key_exchange{},
State = #state{role = client, negotiated_version = Version,
key_algorithm = Alg})
- when Alg == rsa; Alg == dh_dss; Alg == dh_rsa ->
+ when Alg == rsa; Alg == dh_dss; Alg == dh_rsa ->
Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE),
handle_own_alert(Alert, Version, certify_server_key_exchange, State),
{stop, normal, State};
-certify(KeyExchangeMsg = #server_key_exchange{}, State =
- #state{role = server}) ->
- NewState = handle_clinet_key(KeyExchangeMsg, State),
- {next_state, cipher, NewState};
-
certify(#certificate_request{}, State) ->
NewState = State#state{client_certificate_requested = true},
{next_state, certify, next_record(NewState)};
+
+%% Master secret was determined with help of server-key exchange msg
+certify(#server_hello_done{},
+ #state{session = #session{master_secret = MasterSecret} = Session,
+ connection_states = ConnectionStates0,
+ negotiated_version = Version,
+ premaster_secret = undefined,
+ role = client} = State0) ->
+ case ssl_handshake:master_secret(Version, Session,
+ ConnectionStates0, client) of
+ {MasterSecret, ConnectionStates1} ->
+ State = State0#state{connection_states = ConnectionStates1},
+ client_certify_and_key_exchange(State);
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version,
+ certify_server_hello_done, State0),
+ {stop, normal, State0}
+ end;
+
+%% Master secret is calculated from premaster_secret
certify(#server_hello_done{},
#state{session = Session0,
connection_states = ConnectionStates0,
@@ -487,7 +522,8 @@ certify(#client_key_exchange{},
negotiated_version = Version}) ->
%% We expect a certificate here
Alert = ?ALERT_REC(?FATAL, ?UNEXPECTED_MESSAGE),
- handle_own_alert(Alert, Version, certify_server_waiting_certificate, State),
+ handle_own_alert(Alert, Version,
+ certify_server_waiting_certificate, State),
{stop, normal, State};
@@ -517,11 +553,41 @@ certify(#client_key_exchange{exchange_keys
handle_own_alert(Alert, Version, certify_client_key_exchange,
State0),
{stop, normal, State0}
+ end;
+
+certify(#client_key_exchange{exchange_keys = #client_diffie_hellman_public{
+ dh_public = ClientPublicDhKey}},
+ #state{negotiated_version = Version,
+ diffie_hellman_params = #'DHParameter'{prime = P,
+ base = G},
+ diffie_hellman_keys = {_, ServerDhPrivateKey},
+ role = Role,
+ session = Session,
+ connection_states = ConnectionStates0} = State0) ->
+
+ PMpint = crypto:mpint(P),
+ GMpint = crypto:mpint(G),
+ PremasterSecret = crypto:dh_compute_key(mpint_binary(ClientPublicDhKey),
+ ServerDhPrivateKey,
+ [PMpint, GMpint]),
+
+ case ssl_handshake:master_secret(Version, PremasterSecret,
+ ConnectionStates0, Role) of
+ {MasterSecret, ConnectionStates} ->
+ State = State0#state{session =
+ Session#session{master_secret
+ = MasterSecret},
+ connection_states = ConnectionStates},
+ {next_state, cipher, next_record(State)};
+ #alert{} = Alert ->
+ handle_own_alert(Alert, Version,
+ certify_client_key_exchange, State0),
+ {stop, normal, State0}
end.
cipher(socket_control, #state{role = server} = State) ->
{next_state, cipher, State};
-cipher(hello, State) ->
+cipher(#hello_request{}, State) ->
{next_state, cipher, State};
cipher(#certificate_verify{signature = Signature},
@@ -543,43 +609,41 @@ cipher(#certificate_verify{signature = Signature},
end;
cipher(#finished{} = Finished,
- State = #state{from = From,
- negotiated_version = Version,
- host = Host,
- port = Port,
- role = Role,
- session = #session{master_secret = MasterSecret}
- = Session0,
- tls_handshake_hashes = Hashes}) ->
-
+ #state{negotiated_version = Version,
+ host = Host,
+ port = Port,
+ role = Role,
+ session = #session{master_secret = MasterSecret}
+ = Session0,
+ tls_handshake_hashes = Hashes} = State0) ->
+
case ssl_handshake:verify_connection(Version, Finished,
opposite_role(Role),
MasterSecret, Hashes) of
verified ->
- gen_fsm:reply(From, connected),
+ State = ack_connection(State0),
Session = register_session(Role, Host, Port, Session0),
case Role of
client ->
- {next_state, connection,
- next_record_if_active(State#state{session = Session})};
+ next_state_connection(State#state{session = Session});
server ->
{NewConnectionStates, NewHashes} =
finalize_server_handshake(State#state{
session = Session}),
- NewState =
- State#state{connection_states = NewConnectionStates,
- session = Session,
- tls_handshake_hashes = NewHashes},
- {next_state, connection, next_record_if_active(NewState)}
+ next_state_connection(State#state{connection_states =
+ NewConnectionStates,
+ session = Session,
+ tls_handshake_hashes =
+ NewHashes})
end;
#alert{} = Alert ->
- handle_own_alert(Alert, Version, cipher, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, cipher, State0),
+ {stop, normal, State0}
end.
connection(socket_control, #state{role = server} = State) ->
{next_state, connection, State};
-connection(hello, State = #state{host = Host, port = Port,
+connection(#hello_request{}, State = #state{host = Host, port = Port,
socket = Socket,
ssl_options = SslOpts,
negotiated_version = Version,
@@ -592,36 +656,11 @@ connection(hello, State = #state{host = Host, port = Port,
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Hello, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
- {next_state, hello, State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1}}.
-
-%%--------------------------------------------------------------------
-%% Function:
-%% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {reply, Reply, NextStateName, NextState}|
-%% {reply, Reply, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}|
-%% {stop, Reason, Reply, NewState}
-%% Description: There should be one instance of this function for each
-%% possible state name. Whenever a gen_fsm receives an event sent using
-%% gen_fsm:sync_send_event/2,3, the instance of this function with the same
-%% name as the current state name StateName is called to handle the event.
-%%--------------------------------------------------------------------
-connection({application_data, Data}, _From,
- State = #state{socket = Socket,
- negotiated_version = Version,
- transport_cb = Transport,
- connection_states = ConnectionStates0}) ->
- %% We should look into having a worker process to do this to
- %% parallize send and receive decoding and not block the receiver
- %% if sending is overloading the socket.
- {Msgs, ConnectionStates1} = encode_data(Data, Version, ConnectionStates0),
- Result = Transport:send(Socket, Msgs),
- {reply, Result,
- connection, State#state{connection_states = ConnectionStates1}}.
+ {next_state, hello, next_record(State#state{connection_states =
+ ConnectionStates1,
+ tls_handshake_hashes = Hashes1})};
+connection(#client_hello{} = Hello, #state{role = server} = State) ->
+ hello(Hello, State).
%%--------------------------------------------------------------------
%% Function:
@@ -636,22 +675,37 @@ connection({application_data, Data}, _From,
%%--------------------------------------------------------------------
handle_event(#ssl_tls{type = ?HANDSHAKE, fragment = Data},
StateName,
- State = #state{key_algorithm = KeyAlg,
- tls_handshake_buffer = Buf0,
- negotiated_version = Version}) ->
+ State0 = #state{key_algorithm = KeyAlg,
+ tls_handshake_buffer = Buf0,
+ negotiated_version = Version}) ->
Handle =
- fun({Packet, Raw}, {next_state, SName, AS=#state{tls_handshake_hashes=Hs0}}) ->
+ fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Starts new handshake (renegotiation)
+ Hs0 = ssl_handshake:init_hashes(),
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0,
+ renegotiation = {true, peer}});
+ ({#hello_request{} = Packet, _}, {next_state, SName, State}) ->
+ %% This message should not be included in handshake
+ %% message hashes. Already in negotiation so it will be ignored!
+ ?MODULE:SName(Packet, State);
+ ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) ->
+ Hs0 = ssl_handshake:init_hashes(),
Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
- ?MODULE:SName(Packet, AS#state{tls_handshake_hashes=Hs1});
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1,
+ renegotiation = {true, peer}});
+ ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) ->
+ Hs1 = ssl_handshake:update_hashes(Hs0, Raw),
+ ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1});
(_, StopState) -> StopState
end,
try
{Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0, KeyAlg,Version),
- Start = {next_state, StateName, State#state{tls_handshake_buffer = Buf}},
+ Start = {next_state, StateName, State0#state{tls_handshake_buffer = Buf}},
lists:foldl(Handle, Start, Packets)
catch throw:#alert{} = Alert ->
- handle_own_alert(Alert, Version, StateName, State),
- {stop, normal, State}
+ handle_own_alert(Alert, Version, StateName, State0),
+ {stop, normal, State0}
end;
handle_event(#ssl_tls{type = ?APPLICATION_DATA, fragment = Data},
@@ -680,7 +734,8 @@ handle_event(#ssl_tls{type = ?ALERT, fragment = Data}, StateName, State) ->
{next_state, StateName, State};
handle_event(#alert{level = ?FATAL} = Alert, connection,
- #state{from = From, user_application = {_Mon, Pid}, log_alert = Log,
+ #state{from = From, user_application = {_Mon, Pid},
+ log_alert = Log,
host = Host, port = Port, session = Session,
role = Role, socket_options = Opts} = State) ->
invalidate_session(Role, Host, Port, Session),
@@ -706,11 +761,21 @@ handle_event(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert,
_, #state{from = From, role = Role} = State) ->
alert_user(From, Alert, Role),
{stop, normal, State};
-handle_event(#alert{level = ?WARNING} = Alert, StateName,
+
+handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{log_alert = Log, renegotiation = {true, internal}} = State) ->
+ log_alert(Log, StateName, Alert),
+ {stop, normal, State};
+
+handle_event(#alert{level = ?WARNING, description = ?NO_RENEGOTIATION} = Alert, StateName,
+ #state{log_alert = Log, renegotiation = {true, From}} = State) ->
+ log_alert(Log, StateName, Alert),
+ gen_fsm:reply(From, {error, renegotiation_rejected}),
+ {next_state, connection, next_record(State)};
+
+handle_event(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, StateName,
#state{log_alert = Log} = State) ->
log_alert(Log, StateName, Alert),
-%%TODO: Could be user_canceled or no_negotiation should the latter be
- %% treated as fatal?!
{next_state, StateName, next_record(State)}.
%%--------------------------------------------------------------------
@@ -728,6 +793,43 @@ handle_event(#alert{level = ?WARNING} = Alert, StateName,
%% gen_fsm:sync_send_all_state_event/2,3, this function is called to handle
%% the event.
%%--------------------------------------------------------------------
+handle_sync_event({application_data, Data0}, From, connection,
+ #state{socket = Socket,
+ negotiated_version = Version,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ send_queue = SendQueue,
+ socket_options = SockOpts,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}}
+ = State) ->
+ %% We should look into having a worker process to do this to
+ %% parallize send and receive decoding and not block the receiver
+ %% if sending is overloading the socket.
+ try
+ Data = encode_packet(Data0, SockOpts),
+ case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of
+ {Msgs, [], ConnectionStates} ->
+ Result = Transport:send(Socket, Msgs),
+ {reply, Result,
+ connection, State#state{connection_states = ConnectionStates}};
+ {Msgs, RestData, ConnectionStates} ->
+ if
+ Msgs =/= [] ->
+ Transport:send(Socket, Msgs);
+ true ->
+ ok
+ end,
+ renegotiate(State#state{connection_states = ConnectionStates,
+ send_queue = queue:in_r({From, RestData}, SendQueue),
+ renegotiation = {true, internal}})
+ end
+ catch throw:Error ->
+ {reply, Error, connection, State}
+ end;
+handle_sync_event({application_data, Data}, From, StateName,
+ #state{send_queue = Queue} = State) ->
+ %% In renegotiation priorities handshake, send data when handshake is finished
+ {next_state, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}};
handle_sync_event(started, From, StateName, State) ->
{next_state, StateName, State#state{from = From}};
@@ -744,23 +846,14 @@ handle_sync_event({shutdown, How}, From, StateName,
{stop, normal, Error, State#state{from = From}}
end;
-%% TODO: men vad g�r next_record om det �r t.ex. renegotiate? kanske
-%% inte bra... t�l att t�nkas p�!
-handle_sync_event({recv, N}, From, StateName,
- State0 = #state{user_data_buffer = Buffer}) ->
- State1 = State0#state{bytes_to_read = N, from = From},
- case Buffer of
- <<>> ->
- State = next_record(State1),
- {next_state, StateName, State};
- _ ->
- case application_data(<<>>, State1) of
- Stop = {stop, _, _} ->
- Stop;
- State ->
- {next_state, StateName, State}
- end
- end;
+handle_sync_event({recv, N}, From, connection = StateName, State0) ->
+ passive_receive(State0#state{bytes_to_read = N, from = From}, StateName);
+
+%% Doing renegotiate wait with handling request until renegotiate is
+%% finished. Will be handled by next_state_connection/1.
+handle_sync_event({recv, N}, From, StateName, State) ->
+ {next_state, StateName, State#state{bytes_to_read = N, from = From,
+ recv_during_renegotiation = true}};
handle_sync_event({new_user, User}, _From, StateName,
State =#state{user_application = {OldMon, _}}) ->
@@ -808,6 +901,12 @@ handle_sync_event({set_opts, Opts0}, _From, StateName,
end
end;
+handle_sync_event(renegotiate, From, connection, State) ->
+ renegotiate(State#state{renegotiation = {true, From}});
+
+handle_sync_event(renegotiate, _, StateName, State) ->
+ {reply, {error, already_renegotiating}, StateName, State};
+
handle_sync_event(info, _, StateName,
#state{negotiated_version = Version,
session = #session{cipher_suite = Suite}} = State) ->
@@ -828,7 +927,6 @@ handle_sync_event(peer_certificate, _, StateName,
= State) ->
{reply, {ok, Cert}, StateName, State}.
-
%%--------------------------------------------------------------------
%% Function:
%% handle_info(Info,StateName,State)-> {next_state, NextStateName, NextState}|
@@ -857,34 +955,6 @@ handle_info({Protocol, _, Data}, StateName, State =
{stop, normal, State}
end;
-%% %% This is the code for {packet,ssl} removed because it was slower
-%% %% than handling it in erlang.
-%% handle_info(Data = #ssl_tls{}, StateName,
-%% State = #state{tls_buffer = Buffer,
-%% socket = Socket,
-%% connection_states = ConnectionStates0}) ->
-%% case Buffer of
-%% buffer ->
-%% {next_state, StateName, State#state{tls_buffer = [Data]}};
-%% continue ->
-%% inet:setopts(Socket, [{active,once}]),
-%% {Plain, ConnectionStates} =
-%% ssl_record:decode_cipher_text(Data, ConnectionStates0),
-%% gen_fsm:send_all_state_event(self(), Plain),
-%% {next_state, StateName,
-%% State#state{tls_buffer = buffer,
-%% connection_states = ConnectionStates}};
-%% List when is_list(List) ->
-%% {next_state, StateName,
-%% State#state{tls_buffer = Buffer ++ [Data]}}
-%% end;
-
-%% handle_info(CloseMsg = {_, Socket}, StateName0,
-%% #state{socket = Socket,tls_buffer = [Msg]} = State0) ->
-%% %% Hmm we have a ssl_tls msg buffered, handle that first
-%% %% and it proberbly is a close alert
-%% {next_state, StateName0, State0#state{tls_buffer=[Msg,{ssl_close,CloseMsg}]}};
-
handle_info({CloseTag, Socket}, _StateName,
#state{socket = Socket, close_tag = CloseTag,
negotiated_version = Version, host = Host,
@@ -918,17 +988,23 @@ handle_info(A, StateName, State) ->
%% necessary cleaning up. When it returns, the gen_fsm terminates with
%% Reason. The return value is ignored.
%%--------------------------------------------------------------------
-terminate(_Reason, connection, _S=#state{negotiated_version = Version,
+terminate(_Reason, connection, #state{negotiated_version = Version,
connection_states = ConnectionStates,
transport_cb = Transport,
- socket = Socket}) ->
+ socket = Socket, send_queue = SendQueue,
+ renegotiation = Renegotiate}) ->
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate),
{BinAlert, _} = encode_alert(?ALERT_REC(?WARNING,?CLOSE_NOTIFY),
Version, ConnectionStates),
Transport:send(Socket, BinAlert),
Transport:close(Socket);
-terminate(_Reason, _StateName, _S=#state{transport_cb = Transport, socket = Socket}) ->
- Transport:close(Socket),
- ok.
+terminate(_Reason, _StateName, #state{transport_cb = Transport,
+ socket = Socket, send_queue = SendQueue,
+ renegotiation = Renegotiate}) ->
+ notify_senders(SendQueue),
+ notify_renegotiater(Renegotiate),
+ Transport:close(Socket).
%%--------------------------------------------------------------------
%% Function:
@@ -963,8 +1039,8 @@ ssl_init(SslOpts, Role) ->
PrivateKey =
init_private_key(SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile,
SslOpts#ssl_options.password, Role),
- ?DBG_TERM(PrivateKey),
- {ok, CertDbRef, CacheRef, OwnCert, PrivateKey}.
+ DHParams = init_diffie_hellman(SslOpts#ssl_options.dhfile, Role),
+ {ok, CertDbRef, CacheRef, OwnCert, PrivateKey, DHParams}.
init_certificates(#ssl_options{cacertfile = CACertFile,
certfile = CertFile}, Role) ->
@@ -972,8 +1048,14 @@ init_certificates(#ssl_options{cacertfile = CACertFile,
case ssl_manager:connection_init(CACertFile, Role) of
{ok, CertDbRef, CacheRef} ->
init_certificates(CertDbRef, CacheRef, CertFile, Role);
+ {error, {badmatch, _Error}} ->
+ Report = io_lib:format("SSL: Error ~p Initializing: ~p ~n",
+ [_Error, CACertFile]),
+ error_logger:error_report(Report),
+ throw(ecacertfile);
{error, _Error} ->
- Report = io_lib:format("SSL: Error ~p ~n",[_Error]),
+ Report = io_lib:format("SSL: Error ~p Initializing: ~p ~n",
+ [_Error, CACertFile]),
error_logger:error_report(Report),
throw(ecacertfile)
end.
@@ -990,12 +1072,20 @@ init_certificates(CertDbRef, CacheRef, CertFile, server) ->
try
[OwnCert] = ssl_certificate:file_to_certificats(CertFile),
{ok, CertDbRef, CacheRef, OwnCert}
- catch _E:_R ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~p~n",
- [?LINE, _E,_R, erlang:get_stacktrace()]),
- error_logger:error_report(Report),
- throw(ecertfile)
- end.
+ catch
+ _E:{badmatch, _R={error,_}} ->
+ Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
+ [?LINE, _E,_R, CertFile,
+ erlang:get_stacktrace()]),
+ error_logger:error_report(Report),
+ throw(ecertfile);
+ _E:_R ->
+ Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
+ [?LINE, _E,_R, CertFile,
+ erlang:get_stacktrace()]),
+ error_logger:error_report(Report),
+ throw(ecertfile)
+ end.
init_private_key(undefined, "", _Password, client) ->
undefined;
@@ -1003,42 +1093,50 @@ init_private_key(undefined, KeyFile, Password, _) ->
try
{ok, List} = ssl_manager:cache_pem_file(KeyFile),
[Der] = [Der || Der = {PKey, _ , _} <- List,
- PKey =:= rsa_private_key orelse PKey =:= dsa_private_key],
+ PKey =:= rsa_private_key orelse
+ PKey =:= dsa_private_key],
{ok, Decoded} = public_key:decode_private_key(Der,Password),
Decoded
- catch _E:_R ->
- Report = io_lib:format("SSL: ~p: ~p:~p ~p~n",
- [?LINE, _E,_R, erlang:get_stacktrace()]),
+ catch
+ _E:{badmatch, _R={error,_}} ->
+ Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
+ [?LINE, _E,_R, KeyFile,
+ erlang:get_stacktrace()]),
+ error_logger:error_report(Report),
+ throw(ekeyfile);
+ _E:_R ->
+ Report = io_lib:format("SSL: ~p: ~p:~p ~s~n ~p~n",
+ [?LINE, _E,_R, KeyFile,
+ erlang:get_stacktrace()]),
error_logger:error_report(Report),
throw(ekeyfile)
end;
init_private_key(PrivateKey, _, _,_) ->
PrivateKey.
-send_event(FsmPid, Event) ->
- gen_fsm:send_event(FsmPid, Event).
-
-sync_send_event(FsmPid, Event, Timeout) ->
- try gen_fsm:sync_send_event(FsmPid, Event, Timeout) of
- Reply ->
- Reply
- catch
- exit:{noproc, _} ->
- {error, closed};
- exit:{timeout, _} ->
- {error, timeout};
- exit:{normal, _} ->
- {error, closed}
+init_diffie_hellman(_, client) ->
+ undefined;
+init_diffie_hellman(undefined, _) ->
+ ?DEFAULT_DIFFIE_HELLMAN_PARAMS;
+init_diffie_hellman(DHParamFile, server) ->
+ {ok, List} = ssl_manager:cache_pem_file(DHParamFile),
+ case [Der || Der = {dh_params, _ , _} <- List] of
+ [Der] ->
+ {ok, Decoded} = public_key:decode_dhparams(Der),
+ Decoded;
+ [] ->
+ ?DEFAULT_DIFFIE_HELLMAN_PARAMS
end.
+send_event(FsmPid, Event) ->
+ gen_fsm:send_event(FsmPid, Event).
send_all_state_event(FsmPid, Event) ->
gen_fsm:send_all_state_event(FsmPid, Event).
sync_send_all_state_event(FsmPid, Event) ->
- sync_send_all_state_event(FsmPid, Event, ?DEFAULT_TIMEOUT
-).
+ sync_send_all_state_event(FsmPid, Event, ?DEFAULT_TIMEOUT).
sync_send_all_state_event(FsmPid, Event, Timeout) ->
try gen_fsm:sync_send_all_state_event(FsmPid, Event, Timeout)
@@ -1055,6 +1153,16 @@ sync_send_all_state_event(FsmPid, Event, Timeout) ->
alert_event(Alert) ->
send_all_state_event(self(), Alert).
+%% We do currently not support cipher suites that use fixed DH.
+%% If we want to implement that we should add a code
+%% here to extract DH parameters form cert.
+handle_peer_cert(PeerCert, PublicKeyInfo,
+ #state{session = Session} = State0) ->
+ State = State0#state{session =
+ Session#session{peer_certificate = PeerCert},
+ public_key_info = PublicKeyInfo},
+ {next_state, certify, next_record(State)}.
+
certify_client(#state{client_certificate_requested = true, role = client,
connection_states = ConnectionStates0,
transport_cb = Transport,
@@ -1088,9 +1196,8 @@ verify_client_cert(#state{client_certificate_requested = true, role = client,
ignore -> %% No key or cert or fixed_diffie_hellman
State;
Verified ->
- SigAlg = ssl_handshake:sig_alg(KeyAlg),
{BinVerified, ConnectionStates1, Hashes1} =
- encode_handshake(Verified, SigAlg, Version,
+ encode_handshake(Verified, KeyAlg, Version,
ConnectionStates0, Hashes0),
Transport:send(Socket, BinVerified),
State#state{connection_states = ConnectionStates1,
@@ -1115,13 +1222,14 @@ do_server_hello(Type, #state{negotiated_version = Version,
case ssl_handshake:master_secret(Version, Session,
ConnectionStates0, server) of
{_, ConnectionStates1} ->
- {ConnectionStates, Hashes} =
- finished(State#state{connection_states =
- ConnectionStates1}),
- {next_state, abbreviated,
- next_record(State#state{connection_states =
- ConnectionStates,
- tls_handshake_hashes = Hashes})};
+ State1 = State#state{connection_states=ConnectionStates1,
+ session = Session},
+ {ConnectionStates, Hashes} =
+ finalize_server_handshake(State1),
+ Resumed = State1#state{connection_states =
+ ConnectionStates,
+ tls_handshake_hashes = Hashes},
+ {next_state, abbreviated, next_record(Resumed)};
#alert{} = Alert ->
handle_own_alert(Alert, Version, hello, State),
{stop, normal, State}
@@ -1230,14 +1338,15 @@ key_exchange(#state{role = server, key_algorithm = Algo} = State)
Algo == dh_rsa ->
State;
-key_exchange(#state{role = server, key_algorithm = rsa_export} = State) ->
+%key_exchange(#state{role = server, key_algorithm = rsa_export} = State) ->
%% TODO when the public key in the server certificate is
%% less than or equal to 512 bits in length dont send key_exchange
%% but do it otherwise
- State;
+% State;
key_exchange(#state{role = server, key_algorithm = Algo,
diffie_hellman_params = Params,
+ private_key = PrivateKey,
connection_states = ConnectionStates0,
negotiated_version = Version,
tls_handshake_hashes = Hashes0,
@@ -1248,26 +1357,28 @@ key_exchange(#state{role = server, key_algorithm = Algo,
Algo == dhe_dss_export;
Algo == dhe_rsa;
Algo == dhe_rsa_export ->
- Msg = ssl_handshake:key_exchange(server, Params),
- {BinMsg, ConnectionStates1, Hashes1} =
- encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
- Transport:send(Socket, BinMsg),
- State#state{connection_states = ConnectionStates1,
- tls_handshake_hashes = Hashes1};
-key_exchange(#state{role = server, key_algorithm = dh_anon,
- connection_states = ConnectionStates0,
- negotiated_version = Version,
- tls_handshake_hashes = Hashes0,
- socket = Socket,
- transport_cb = Transport
- } = State) ->
- Msg = ssl_handshake:key_exchange(server, anonymous),
- {BinMsg, ConnectionStates1, Hashes1} =
+ Keys = public_key:gen_key(Params),
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates0, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ Msg = ssl_handshake:key_exchange(server, {dh, Keys, Params,
+ Algo, ClientRandom,
+ ServerRandom,
+ PrivateKey}),
+ {BinMsg, ConnectionStates, Hashes1} =
encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
- State#state{connection_states = ConnectionStates1,
+ State#state{connection_states = ConnectionStates,
+ diffie_hellman_keys = Keys,
tls_handshake_hashes = Hashes1};
+
+
+%% key_algorithm = dh_anon is not supported. Should be by default disabled
+%% if support is implemented and then we need a key_exchange clause for it
+%% here.
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
@@ -1287,17 +1398,33 @@ key_exchange(#state{role = client,
key_exchange(#state{role = client,
connection_states = ConnectionStates0,
key_algorithm = Algorithm,
- public_key_info = PublicKeyInfo,
negotiated_version = Version,
- diffie_hellman_params = Params,
- own_cert = Cert,
+ diffie_hellman_keys = {DhPubKey, _},
socket = Socket, transport_cb = Transport,
tls_handshake_hashes = Hashes0} = State)
when Algorithm == dhe_dss;
Algorithm == dhe_dss_export;
Algorithm == dhe_rsa;
Algorithm == dhe_rsa_export ->
- Msg = dh_key_exchange(Cert, Params, PublicKeyInfo),
+ Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}),
+ {BinMsg, ConnectionStates1, Hashes1} =
+ encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
+ Transport:send(Socket, BinMsg),
+ State#state{connection_states = ConnectionStates1,
+ tls_handshake_hashes = Hashes1};
+
+key_exchange(#state{role = client,
+ connection_states = ConnectionStates0,
+ key_algorithm = Algorithm,
+ negotiated_version = Version,
+ client_certificate_requested = ClientCertReq,
+ own_cert = OwnCert,
+ diffie_hellman_keys = DhKeys,
+ socket = Socket, transport_cb = Transport,
+ tls_handshake_hashes = Hashes0} = State)
+ when Algorithm == dh_dss;
+ Algorithm == dh_rsa ->
+ Msg = dh_key_exchange(OwnCert, DhKeys, ClientCertReq),
{BinMsg, ConnectionStates1, Hashes1} =
encode_handshake(Msg, Version, ConnectionStates0, Hashes0),
Transport:send(Socket, BinMsg),
@@ -1312,17 +1439,19 @@ rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _})
ssl_handshake:key_exchange(client,
{premaster_secret, PremasterSecret,
PublicKeyInfo});
-
rsa_key_exchange(_, _) ->
throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)).
-dh_key_exchange(OwnCert, Params, PublicKeyInfo) ->
+dh_key_exchange(OwnCert, DhKeys, true) ->
case public_key:pkix_is_fixed_dh_cert(OwnCert) of
true ->
ssl_handshake:key_exchange(client, fixed_diffie_hellman);
false ->
- ssl_handshake:key_exchange(client, {dh, Params, PublicKeyInfo})
- end.
+ {DhPubKey, _} = DhKeys,
+ ssl_handshake:key_exchange(client, {dh, DhPubKey})
+ end;
+dh_key_exchange(_, {DhPubKey, _}, false) ->
+ ssl_handshake:key_exchange(client, {dh, DhPubKey}).
request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer},
connection_states = ConnectionStates0,
@@ -1356,7 +1485,8 @@ finalize_client_handshake(#state{connection_states = ConnectionStates0}
finalize_server_handshake(State) ->
ConnectionStates0 = cipher_protocol(State),
ConnectionStates =
- ssl_record:activate_pending_connection_state(ConnectionStates0, write),
+ ssl_record:activate_pending_connection_state(ConnectionStates0,
+ write),
finished(State#state{connection_states = ConnectionStates}).
cipher_protocol(#state{connection_states = ConnectionStates,
@@ -1364,7 +1494,8 @@ cipher_protocol(#state{connection_states = ConnectionStates,
negotiated_version = Version,
transport_cb = Transport}) ->
{BinChangeCipher, NewConnectionStates} =
- encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates),
+ encode_change_cipher(#change_cipher_spec{},
+ Version, ConnectionStates),
Transport:send(Socket, BinChangeCipher),
NewConnectionStates.
@@ -1380,10 +1511,66 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version,
Transport:send(Socket, BinFinished),
{NewConnectionStates, NewHashes}.
-handle_server_key(_KeyExchangeMsg, State) ->
- State.
-handle_clinet_key(_KeyExchangeMsg, State) ->
- State.
+handle_server_key(
+ #server_key_exchange{params =
+ #server_dh_params{dh_p = P,
+ dh_g = G,
+ dh_y = ServerPublicDhKey},
+ signed_params = Signed},
+ #state{session = Session, negotiated_version = Version, role = Role,
+ public_key_info = PubKeyInfo,
+ key_algorithm = KeyAlgo,
+ connection_states = ConnectionStates0} = State) ->
+
+ PLen = size(P),
+ GLen = size(G),
+ YLen = size(ServerPublicDhKey),
+
+ ConnectionState =
+ ssl_record:pending_connection_state(ConnectionStates0, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo,
+ <<ClientRandom/binary,
+ ServerRandom/binary,
+ ?UINT16(PLen), P/binary,
+ ?UINT16(GLen), G/binary,
+ ?UINT16(YLen),
+ ServerPublicDhKey/binary>>),
+
+ case verify_dh_params(Signed, Hash, PubKeyInfo) of
+ true ->
+ PMpint = mpint_binary(P),
+ GMpint = mpint_binary(G),
+ Keys = {_, ClientDhPrivateKey} =
+ crypto:dh_generate_key([PMpint,GMpint]),
+ PremasterSecret =
+ crypto:dh_compute_key(mpint_binary(ServerPublicDhKey),
+ ClientDhPrivateKey, [PMpint, GMpint]),
+ case ssl_handshake:master_secret(Version, PremasterSecret,
+ ConnectionStates0, Role) of
+ {MasterSecret, ConnectionStates} ->
+ State#state{diffie_hellman_keys = Keys,
+ session =
+ Session#session{master_secret
+ = MasterSecret},
+ connection_states = ConnectionStates};
+ #alert{} = Alert ->
+ Alert
+ end;
+ false ->
+ ?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)
+ end.
+
+verify_dh_params(Signed, Hash, {?rsaEncryption, PubKey, _PubKeyparams}) ->
+ case public_key:decrypt_public(Signed, PubKey,
+ [{rsa_pad, rsa_pkcs1_padding}]) of
+ Hash ->
+ true;
+ _ ->
+ false
+ end.
encode_alert(#alert{} = Alert, Version, ConnectionStates) ->
?DBG_TERM(Alert),
@@ -1405,8 +1592,23 @@ encode_handshake(HandshakeRec, SigAlg, Version, ConnectionStates0, Hashes0) ->
ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
{E, ConnectionStates1, Hashes1}.
-encode_data(Data, Version, ConnectionStates) ->
- ssl_record:encode_data(Data, Version, ConnectionStates).
+encode_packet(Data, #socket_options{packet=Packet}) ->
+ case Packet of
+ 1 -> encode_size_packet(Data, 8, (1 bsl 8) - 1);
+ 2 -> encode_size_packet(Data, 16, (1 bsl 16) - 1);
+ 4 -> encode_size_packet(Data, 32, (1 bsl 32) - 1);
+ _ -> Data
+ end.
+
+encode_size_packet(Bin, Size, Max) ->
+ Len = byte_size(Bin),
+ case Len > Max of
+ true -> throw({error, {badarg, {packet_to_large, Len, Max}}});
+ false -> <<Len:Size, Bin/binary>>
+ end.
+
+encode_data(Data, Version, ConnectionStates, RenegotiateAt) ->
+ ssl_record:encode_data(Data, Version, ConnectionStates, RenegotiateAt).
decode_alerts(Bin) ->
decode_alerts(Bin, []).
@@ -1417,6 +1619,20 @@ decode_alerts(<<?BYTE(Level), ?BYTE(Description), Rest/binary>>, Acc) ->
decode_alerts(<<>>, Acc) ->
lists:reverse(Acc, []).
+passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) ->
+ case Buffer of
+ <<>> ->
+ State = next_record(State0),
+ {next_state, StateName, State};
+ _ ->
+ case application_data(<<>>, State0) of
+ Stop = {stop, _, _} ->
+ Stop;
+ State ->
+ {next_state, StateName, State}
+ end
+ end.
+
application_data(Data, #state{user_application = {_Mon, Pid},
socket_options = SOpts,
bytes_to_read = BytesToRead,
@@ -1467,19 +1683,49 @@ get_data(#socket_options{active=Active, packet=Raw}, BytesToRead, Buffer)
end;
get_data(#socket_options{packet=Type, packet_size=Size}, _, Buffer) ->
PacketOpts = [{packet_size, Size}],
- case erlang:decode_packet(Type, Buffer, PacketOpts) of
+ case decode_packet(Type, Buffer, PacketOpts) of
{more, _} ->
{ok, <<>>, Buffer};
Decoded ->
Decoded
end.
-deliver_app_data(SO = #socket_options{active=once}, Data, Pid, From) ->
- send_or_reply(once, Pid, From, format_reply(SO, Data)),
- SO#socket_options{active=false};
-deliver_app_data(SO= #socket_options{active=Active}, Data, Pid, From) ->
- send_or_reply(Active, Pid, From, format_reply(SO, Data)),
- SO.
+decode_packet({http, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph, Buffer, PacketOpts);
+decode_packet({http_bin, headers}, Buffer, PacketOpts) ->
+ decode_packet(httph_bin, Buffer, PacketOpts);
+decode_packet(Type, Buffer, PacketOpts) ->
+ erlang:decode_packet(Type, Buffer, PacketOpts).
+
+%% Just like with gen_tcp sockets, an ssl socket that has been configured with
+%% {packet, http} (or {packet, http_bin}) will automatically switch to expect
+%% HTTP headers after it sees a HTTP Request or HTTP Response line. We
+%% represent the current state as follows:
+%% #socket_options.packet =:= http: Expect a HTTP Request/Response line
+%% #socket_options.packet =:= {http, headers}: Expect HTTP Headers
+%% Note that if the user has explicitly configured the socket to expect
+%% HTTP headers using the {packet, httph} option, we don't do any automatic
+%% switching of states.
+deliver_app_data(SOpts = #socket_options{active=Active, packet=Type},
+ Data, Pid, From) ->
+ send_or_reply(Active, Pid, From, format_reply(SOpts, Data)),
+ SO = case Data of
+ {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)),
+ ((Type =:= http) or (Type =:= http_bin)) ->
+ SOpts#socket_options{packet={Type, headers}};
+ http_eoh when tuple_size(Type) =:= 2 ->
+ % End of headers - expect another Request/Response line
+ {Type1, headers} = Type,
+ SOpts#socket_options{packet=Type1};
+ _ ->
+ SOpts
+ end,
+ case Active of
+ once ->
+ SO#socket_options{active=false};
+ _ ->
+ SO
+ end.
format_reply(#socket_options{active=false, mode=Mode, header=Header}, Data) ->
{ok, format_reply(Mode, Header, Data)};
@@ -1520,34 +1766,6 @@ opposite_role(server) ->
send_user(Pid, Msg) ->
Pid ! Msg.
-%% %% This is the code for {packet,ssl} removed because it was slower
-%% %% than handling it in erlang.
-%% next_record(#state{socket = Socket,
-%% tls_buffer = [Msg|Rest],
-%% connection_states = ConnectionStates0} = State) ->
-%% Buffer =
-%% case Rest of
-%% [] ->
-%% inet:setopts(Socket, [{active,once}]),
-%% buffer;
-%% _ -> Rest
-%% end,
-%% case Msg of
-%% #ssl_tls{} ->
-%% {Plain, ConnectionStates} =
-%% ssl_record:decode_cipher_text(Msg, ConnectionStates0),
-%% gen_fsm:send_all_state_event(self(), Plain),
-%% State#state{tls_buffer=Buffer, connection_states = ConnectionStates};
-%% {ssl_close, Msg} ->
-%% self() ! Msg,
-%% State#state{tls_buffer=Buffer}
-%% end;
-%% next_record(#state{socket = Socket, tls_buffer = undefined} = State) ->
-%% inet:setopts(Socket, [{active,once}]),
-%% State#state{tls_buffer=continue};
-%% next_record(State) ->
-%% State#state{tls_buffer=continue}.
-
next_record(#state{tls_cipher_texts = [], socket = Socket} = State) ->
inet:setopts(Socket, [{active,once}]),
State;
@@ -1557,13 +1775,57 @@ next_record(#state{tls_cipher_texts = [CT | Rest],
gen_fsm:send_all_state_event(self(), Plain),
State#state{tls_cipher_texts = Rest, connection_states = ConnStates}.
+
next_record_if_active(State =
#state{socket_options =
#socket_options{active = false}}) ->
State;
+
next_record_if_active(State) ->
next_record(State).
+next_state_connection(#state{send_queue = Queue0,
+ negotiated_version = Version,
+ socket = Socket,
+ transport_cb = Transport,
+ connection_states = ConnectionStates0,
+ ssl_options = #ssl_options{renegotiate_at = RenegotiateAt}
+ } = State) ->
+ %% Send queued up data
+ case queue:out(Queue0) of
+ {{value, {From, Data}}, Queue} ->
+ case encode_data(Data, Version, ConnectionStates0, RenegotiateAt) of
+ {Msgs, [], ConnectionStates} ->
+ Result = Transport:send(Socket, Msgs),
+ gen_fsm:reply(From, Result),
+ next_state_connection(State#state{connection_states = ConnectionStates,
+ send_queue = Queue});
+ %% This is unlikely to happen. User configuration of the
+ %% undocumented test option renegotiation_at can make it more likely.
+ {Msgs, RestData, ConnectionStates} ->
+ if
+ Msgs =/= [] ->
+ Transport:send(Socket, Msgs);
+ true ->
+ ok
+ end,
+ renegotiate(State#state{connection_states = ConnectionStates,
+ send_queue = queue:in_r({From, RestData}, Queue),
+ renegotiation = {true, internal}})
+ end;
+ {empty, Queue0} ->
+ next_state_is_connection(State)
+ end.
+
+next_state_is_connection(State =
+ #state{recv_during_renegotiation = true, socket_options =
+ #socket_options{active = false}}) ->
+ passive_receive(State#state{recv_during_renegotiation = false}, connection);
+
+next_state_is_connection(State) ->
+ {next_state, connection, next_record_if_active(State)}.
+
+
register_session(_, _, _, #session{is_resumable = true} = Session) ->
Session; %% Already registered
register_session(client, Host, Port, Session0) ->
@@ -1613,7 +1875,10 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User,
bytes_to_read = 0,
user_data_buffer = <<>>,
log_alert = true,
- session_cache_cb = SessionCacheCb
+ session_cache_cb = SessionCacheCb,
+ renegotiation = {false, first},
+ recv_during_renegotiation = false,
+ send_queue = queue:new()
}.
sslsocket(Pid) ->
@@ -1628,8 +1893,12 @@ get_socket_opts(Socket, [mode | Tags], SockOpts, Acc) ->
get_socket_opts(Socket, Tags, SockOpts,
[{mode, SockOpts#socket_options.mode} | Acc]);
get_socket_opts(Socket, [packet | Tags], SockOpts, Acc) ->
- get_socket_opts(Socket, Tags, SockOpts,
- [{packet, SockOpts#socket_options.packet} | Acc]);
+ case SockOpts#socket_options.packet of
+ {Type, headers} ->
+ get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc]);
+ Type ->
+ get_socket_opts(Socket, Tags, SockOpts, [{packet, Type} | Acc])
+ end;
get_socket_opts(Socket, [header | Tags], SockOpts, Acc) ->
get_socket_opts(Socket, Tags, SockOpts,
[{header, SockOpts#socket_options.header} | Acc]);
@@ -1651,7 +1920,8 @@ set_socket_opts(Socket, [], SockOpts, Other) ->
inet:setopts(Socket, Other),
SockOpts;
set_socket_opts(Socket, [{mode, Mode}| Opts], SockOpts, Other) ->
- set_socket_opts(Socket, Opts, SockOpts#socket_options{mode = Mode}, Other);
+ set_socket_opts(Socket, Opts,
+ SockOpts#socket_options{mode = Mode}, Other);
set_socket_opts(Socket, [{packet, Packet}| Opts], SockOpts, Other) ->
set_socket_opts(Socket, Opts,
SockOpts#socket_options{packet = Packet}, Other);
@@ -1693,12 +1963,68 @@ handle_own_alert(Alert, Version, StateName,
role = Role,
connection_states = ConnectionStates,
log_alert = Log}) ->
- {BinMsg, _} =
- encode_alert(Alert, Version, ConnectionStates),
- Transport:send(Socket, BinMsg),
- log_alert(Log, StateName, Alert),
- alert_user(User, Alert, Role).
-
-make_premaster_secret({MajVer, MinVer}) ->
+ try %% Try to tell the other side
+ {BinMsg, _} =
+ encode_alert(Alert, Version, ConnectionStates),
+ Transport:send(Socket, BinMsg)
+ catch _:_ -> %% Can crash if we are in a uninitialized state
+ ignore
+ end,
+ try %% Try to tell the local user
+ log_alert(Log, StateName, Alert),
+ alert_user(User, Alert, Role)
+ catch _:_ ->
+ ok
+ end.
+make_premaster_secret({MajVer, MinVer}, Alg) when Alg == rsa;
+ Alg == dh_dss;
+ Alg == dh_rsa ->
Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2),
- <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>.
+ <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>;
+make_premaster_secret(_, _) ->
+ undefined.
+
+mpint_binary(Binary) ->
+ Size = byte_size(Binary),
+ <<?UINT32(Size), Binary/binary>>.
+
+
+ack_connection(#state{renegotiation = {true, Initiater}} = State)
+ when Initiater == internal;
+ Initiater == peer ->
+ State#state{renegotiation = undefined};
+ack_connection(#state{renegotiation = {true, From}} = State) ->
+ gen_fsm:reply(From, ok),
+ State#state{renegotiation = undefined};
+ack_connection(#state{renegotiation = {false, first}, from = From} = State) ->
+ gen_fsm:reply(From, connected),
+ State#state{renegotiation = undefined}.
+
+renegotiate(#state{role = client} = State) ->
+ %% Handle same way as if server requested
+ %% the renegotiation
+ Hs0 = ssl_handshake:init_hashes(),
+ connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0});
+renegotiate(#state{role = server,
+ socket = Socket,
+ transport_cb = Transport,
+ negotiated_version = Version,
+ connection_states = ConnectionStates0} = State) ->
+ HelloRequest = ssl_handshake:hello_request(),
+ Frag = ssl_handshake:encode_handshake(HelloRequest, Version, undefined),
+ Hs0 = ssl_handshake:init_hashes(),
+ {BinMsg, ConnectionStates} =
+ ssl_record:encode_handshake(Frag, Version, ConnectionStates0),
+ Transport:send(Socket, BinMsg),
+ {next_state, hello, next_record(State#state{connection_states =
+ ConnectionStates,
+ tls_handshake_hashes = Hs0})}.
+notify_senders(SendQueue) ->
+ lists:foreach(fun({From, _}) ->
+ gen_fsm:reply(From, {error, closed})
+ end, queue:to_list(SendQueue)).
+
+notify_renegotiater({true, From}) when not is_atom(From) ->
+ gen_fsm:reply(From, {error, closed});
+notify_renegotiater(_) ->
+ ok.
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 829e0c2ba6..9f5ac7106a 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -31,11 +31,11 @@
-include("ssl_debug.hrl").
-include_lib("public_key/include/public_key.hrl").
--export([master_secret/4, client_hello/4, server_hello/3, hello/2,
- certify/5, certificate/3,
+-export([master_secret/4, client_hello/4, server_hello/3, hello/2,
+ hello_request/0, certify/7, certificate/3,
client_certificate_verify/6,
certificate_verify/6, certificate_request/2,
- key_exchange/2, finished/4,
+ key_exchange/2, server_key_exchange_hash/2, finished/4,
verify_connection/5,
get_tls_handshake/4,
server_hello_done/0, sig_alg/1,
@@ -97,6 +97,15 @@ server_hello(SessionId, Version, ConnectionStates) ->
}.
%%--------------------------------------------------------------------
+%% Function: hello_request() -> #hello_request{}
+%%
+%% Description: Creates a hello request message sent by server to
+%% trigger renegotiation.
+%%--------------------------------------------------------------------
+hello_request() ->
+ #hello_request{}.
+
+%%--------------------------------------------------------------------
%% Function: hello(Hello, Info) ->
%% {Version, Id, NewConnectionStates} |
%% #alert{}
@@ -152,10 +161,25 @@ hello(#client_hello{client_version = ClientVersion, random = Random} = Hello,
%% Description: Handles a certificate handshake message
%%--------------------------------------------------------------------
certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
- MaxPathLen, Verify, VerifyFun) ->
+ MaxPathLen, Verify, VerifyFun, ValidateFun, Role) ->
[PeerCert | _] = ASN1Certs,
VerifyBool = verify_bool(Verify),
-
+
+ ValidateExtensionFun =
+ case ValidateFun of
+ undefined ->
+ fun(Extensions, ValidationState, Verify0, AccError) ->
+ ssl_certificate:validate_extensions(Extensions, ValidationState,
+ [], Verify0, AccError, Role)
+ end;
+ Fun ->
+ fun(Extensions, ValidationState, Verify0, AccError) ->
+ {NewExtensions, NewValidationState, NewAccError}
+ = ssl_certificate:validate_extensions(Extensions, ValidationState,
+ [], Verify0, AccError, Role),
+ Fun(NewExtensions, NewValidationState, Verify0, NewAccError)
+ end
+ end,
try
%% Allow missing root_cert and check that with VerifyFun
ssl_certificate:trusted_cert_and_path(ASN1Certs, CertDbRef, false) of
@@ -165,6 +189,8 @@ certify(#certificate{asn1_certificates = ASN1Certs}, CertDbRef,
[{max_path_length,
MaxPathLen},
{verify, VerifyBool},
+ {validate_extensions_fun,
+ ValidateExtensionFun},
{acc_errors,
VerifyErrors}]),
case Result of
@@ -248,8 +274,10 @@ client_certificate_verify(OwnCert, MasterSecret, Version, Algorithm,
%% Description: Checks that the certificate_verify message is valid.
%%--------------------------------------------------------------------
certificate_verify(Signature, {_, PublicKey, _}, Version,
- MasterSecret, Algorithm, {_, Hashes0})
- when Algorithm =:= rsa; Algorithm =:= dh_rsa; Algorithm =:= dhe_rsa ->
+ MasterSecret, Algorithm, {_, Hashes0})
+ when Algorithm == rsa;
+ Algorithm == dh_rsa;
+ Algorithm == dhe_rsa ->
Hashes = calc_certificate_verify(Version, MasterSecret,
Algorithm, Hashes0),
case public_key:decrypt_public(Signature, PublicKey,
@@ -296,30 +324,32 @@ key_exchange(client, fixed_diffie_hellman) ->
#client_diffie_hellman_public{
dh_public = <<>>
}};
-key_exchange(client, {dh, PublicKey}) ->
- Len = byte_size(PublicKey),
+key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) ->
#client_key_exchange{
- exchange_keys = #client_diffie_hellman_public{
- dh_public = <<?UINT16(Len), PublicKey/binary>>}
+ exchange_keys = #client_diffie_hellman_public{
+ dh_public = PublicKey}
};
-%% key_exchange(server, {{?'dhpublicnumber', _PublicKey,
-%% #'DomainParameters'{p = P, g = G, y = Y},
-%% SignAlgorithm, ClientRandom, ServerRandom}}) ->
-%% ServerDHParams = #server_dh_params{dh_p = P, dh_g = G, dh_y = Y},
-%% PLen = byte_size(P),
-%% GLen = byte_size(G),
-%% YLen = byte_size(Y),
-%% Hash = server_key_exchange_hash(SignAlgorithm, <<ClientRandom/binary,
-%% ServerRandom/binary,
-%% ?UINT16(PLen), P/binary,
-%% ?UINT16(GLen), G/binary,
-%% ?UINT16(YLen), Y/binary>>),
-%% Signed = digitally_signed(Hash, PrivateKey),
-%% #server_key_exchange{
-%% params = ServerDHParams,
-%% signed_params = Signed
-%% };
+key_exchange(server, {dh, {<<?UINT32(_), PublicKey/binary>>, _},
+ #'DHParameter'{prime = P, base = G},
+ KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) ->
+ <<?UINT32(_), PBin/binary>> = crypto:mpint(P),
+ <<?UINT32(_), GBin/binary>> = crypto:mpint(G),
+ PLen = byte_size(PBin),
+ GLen = byte_size(GBin),
+ YLen = byte_size(PublicKey),
+ ServerDHParams = #server_dh_params{dh_p = PBin,
+ dh_g = GBin, dh_y = PublicKey},
+
+ Hash =
+ server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary,
+ ServerRandom/binary,
+ ?UINT16(PLen), PBin/binary,
+ ?UINT16(GLen), GBin/binary,
+ ?UINT16(YLen), PublicKey/binary>>),
+ Signed = digitally_signed(Hash, PrivateKey),
+ #server_key_exchange{params = ServerDHParams,
+ signed_params = Signed};
key_exchange(_, _) ->
%%TODO : Real imp
#server_key_exchange{}.
@@ -417,7 +447,8 @@ server_hello_done() ->
%%
%% encode a handshake packet to binary
%%--------------------------------------------------------------------
-encode_handshake(Package, Version, SigAlg) ->
+encode_handshake(Package, Version, KeyAlg) ->
+ SigAlg = sig_alg(KeyAlg),
{MsgType, Bin} = enc_hs(Package, Version, SigAlg),
Len = byte_size(Bin),
[MsgType, ?uint24(Len), Bin].
@@ -437,32 +468,16 @@ get_tls_handshake(Data, Buffer, KeyAlg, Version) ->
get_tls_handshake_aux(list_to_binary([Buffer, Data]),
KeyAlg, Version, []).
-get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>,
- KeyAlg, Version, Acc) ->
+get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
+ Body:Length/binary,Rest/binary>>, KeyAlg,
+ Version, Acc) ->
Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>,
- H = dec_hs(Type, Body, KeyAlg, Version),
+ H = dec_hs(Type, Body, key_exchange_alg(KeyAlg), Version),
get_tls_handshake_aux(Rest, KeyAlg, Version, [{H,Raw} | Acc]);
get_tls_handshake_aux(Data, _KeyAlg, _Version, Acc) ->
{lists:reverse(Acc), Data}.
%%--------------------------------------------------------------------
-%% Function: sig_alg(atom()) -> integer()
-%%
-%% Description: Convert from key exchange as atom to signature
-%% algorithm as a ?SIGNATURE_... constant
-%%--------------------------------------------------------------------
-
-sig_alg(dh_anon) ->
- ?SIGNATURE_ANONYMOUS;
-sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa ->
- ?SIGNATURE_RSA;
-sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss ->
- ?SIGNATURE_DSA;
-sig_alg(_) ->
- ?NULL.
-
-
-%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
verify_bool(verify_peer) ->
@@ -649,8 +664,8 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary,
dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>, _, _) ->
#certificate{asn1_certificates = certs_to_list(ASN1Certs)};
dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary,
- ?UINT16(ExpLen), Exp:ExpLen/binary,
- Sig/binary>>,
+ ?UINT16(ExpLen), Exp:ExpLen/binary,
+ ?UINT16(_), Sig/binary>>,
?KEY_EXCHANGE_RSA, _) ->
#server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod,
rsa_exponent = Exp},
@@ -658,9 +673,10 @@ dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod:ModLen/binary,
dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
?UINT16(GLen), G:GLen/binary,
?UINT16(YLen), Y:YLen/binary,
- Sig/binary>>,
+ ?UINT16(_), Sig/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y},
+ #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G,
+ dh_y = Y},
signed_params = Sig};
dec_hs(?CERTIFICATE_REQUEST,
<<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary,
@@ -672,18 +688,20 @@ dec_hs(?SERVER_HELLO_DONE, <<>>, _, _) ->
#server_hello_done{};
dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>, _, _)->
#certificate_verify{signature = Signature};
-dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, rsa, {3, 0}) ->
+dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) ->
PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS},
#client_key_exchange{exchange_keys = PreSecret};
-dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>, rsa, _) ->
+dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(_), PKEPMS/binary>>,
+ ?KEY_EXCHANGE_RSA, _) ->
PreSecret = #encrypted_premaster_secret{premaster_secret = PKEPMS},
#client_key_exchange{exchange_keys = PreSecret};
dec_hs(?CLIENT_KEY_EXCHANGE, <<>>, ?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
%% TODO: Should check whether the cert already contains a suitable DH-key (7.4.7.2)
throw(?ALERT_REC(?FATAL, implicit_public_value_encoding));
-dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YCLen), DH_YC:DH_YCLen/binary>>,
+dec_hs(?CLIENT_KEY_EXCHANGE, <<?UINT16(DH_YLen), DH_Y:DH_YLen/binary>>,
?KEY_EXCHANGE_DIFFIE_HELLMAN, _) ->
- #client_diffie_hellman_public{dh_public = DH_YC};
+ #client_key_exchange{exchange_keys =
+ #client_diffie_hellman_public{dh_public = DH_Y}};
dec_hs(?FINISHED, VerifyData, _, _) ->
#finished{verify_data = VerifyData};
dec_hs(_, _, _, _) ->
@@ -756,12 +774,13 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version, _) ->
{?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>};
enc_hs(#server_key_exchange{params = #server_rsa_params{rsa_modulus = Mod,
rsa_exponent = Exp},
- signed_params = SignedParams}, _Version, _) ->
+ signed_params = SignedParams}, _Version, _) ->
ModLen = byte_size(Mod),
ExpLen = byte_size(Exp),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen), Mod/binary,
+ SignedLen = byte_size(SignedParams),
+ {?SERVER_KEY_EXCHANGE, <<?UINT16(ModLen),Mod/binary,
?UINT16(ExpLen), Exp/binary,
- SignedParams/binary>>
+ ?UINT16(SignedLen), SignedParams/binary>>
};
enc_hs(#server_key_exchange{params = #server_dh_params{
dh_p = P, dh_g = G, dh_y = Y},
@@ -769,10 +788,11 @@ enc_hs(#server_key_exchange{params = #server_dh_params{
PLen = byte_size(P),
GLen = byte_size(G),
YLen = byte_size(Y),
- {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary,
- ?UINT16(GLen), G:GLen/binary,
- ?UINT16(YLen), Y:YLen/binary,
- SignedParams/binary>>
+ SignedLen = byte_size(SignedParams),
+ {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary,
+ ?UINT16(GLen), G/binary,
+ ?UINT16(YLen), Y/binary,
+ ?UINT16(SignedLen), SignedParams/binary>>
};
enc_hs(#certificate_request{certificate_types = CertTypes,
certificate_authorities = CertAuths},
@@ -860,9 +880,31 @@ certificate_types(_) ->
%% a RSA_FIXED_DH or DSS_FIXED_DH
<<?BYTE(?RSA_SIGN)>>.
-certificate_authorities(_) ->
- %%TODO Make list of know CA:s
- <<>>.
+certificate_authorities(CertDbRef) ->
+ Authorities = certificate_authorities_from_db(CertDbRef),
+ Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) ->
+ OTPSubj = TBSCert#'OTPTBSCertificate'.subject,
+ Subj = public_key:pkix_transform(OTPSubj, encode),
+ {ok, DNEncoded} = 'OTP-PUB-KEY':encode('Name', Subj),
+ DNEncodedBin = iolist_to_binary(DNEncoded),
+ DNEncodedLen = byte_size(DNEncodedBin),
+ <<?UINT16(DNEncodedLen), DNEncodedBin/binary>>
+ end,
+ list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]).
+
+certificate_authorities_from_db(CertDbRef) ->
+ certificate_authorities_from_db(CertDbRef, no_candidate, []).
+
+certificate_authorities_from_db(CertDbRef, PrevKey, Acc) ->
+ case ssl_certificate_db:issuer_candidate(PrevKey) of
+ no_more_candidates ->
+ lists:reverse(Acc);
+ {{CertDbRef, _, _} = Key, Cert} ->
+ certificate_authorities_from_db(CertDbRef, Key, [Cert|Acc]);
+ {Key, _Cert} ->
+ %% skip certs not from this ssl connection
+ certificate_authorities_from_db(CertDbRef, Key, Acc)
+ end.
digitally_signed(Hashes, #'RSAPrivateKey'{} = Key) ->
public_key:encrypt_private(Hashes, Key,
@@ -905,13 +947,40 @@ calc_certificate_verify({3, N}, _, Algorithm, Hashes)
when N == 1; N == 2 ->
ssl_tls1:certificate_verify(Algorithm, Hashes).
-%% server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
-%% Algorithm == dh_rsa;
-%% Algorithm == dhe_rsa ->
-%% MD5 = crypto:md5_final(Value),
-%% SHA = crypto:sha_final(Value),
-%% <<MD5/binary, SHA/binary>>;
+server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa;
+ Algorithm == dh_rsa;
+ Algorithm == dhe_rsa ->
+ MD5Context = crypto:md5_init(),
+ NewMD5Context = crypto:md5_update(MD5Context, Value),
+ MD5 = crypto:md5_final(NewMD5Context),
+
+ SHAContext = crypto:sha_init(),
+ NewSHAContext = crypto:sha_update(SHAContext, Value),
+ SHA = crypto:sha_final(NewSHAContext),
+
+ <<MD5/binary, SHA/binary>>;
+
+server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss;
+ Algorithm == dhe_dss ->
-%% server_key_exchange_hash(Algorithm, Value) when Algorithm == dh_dss;
-%% Algorithm == dhe_dss ->
-%% crypto:sha_final(Value).
+ SHAContext = crypto:sha_init(),
+ NewSHAContext = crypto:sha_update(SHAContext, Value),
+ crypto:sha_final(NewSHAContext).
+
+
+sig_alg(dh_anon) ->
+ ?SIGNATURE_ANONYMOUS;
+sig_alg(Alg) when Alg == dhe_rsa; Alg == rsa; Alg == dh_rsa ->
+ ?SIGNATURE_RSA;
+sig_alg(Alg) when Alg == dh_dss; Alg == dhe_dss ->
+ ?SIGNATURE_DSA;
+sig_alg(_) ->
+ ?NULL.
+
+key_exchange_alg(rsa) ->
+ ?KEY_EXCHANGE_RSA;
+key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss;
+ Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon ->
+ ?KEY_EXCHANGE_DIFFIE_HELLMAN;
+key_exchange_alg(_) ->
+ ?NULL.
diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl
index b2bdfa0934..889d39f2af 100644
--- a/lib/ssl/src/ssl_handshake.hrl
+++ b/lib/ssl/src/ssl_handshake.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -38,7 +38,8 @@
-define(NUM_OF_SESSION_ID_BYTES, 32). % TSL 1.1 & SSL 3
-define(NUM_OF_PREMASTERSECRET_BYTES, 48).
-
+-define(DEFAULT_DIFFIE_HELLMAN_GENERATOR, 2).
+-define(DEFAULT_DIFFIE_HELLMAN_PRIME, 16#FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Handsake protocol - RFC 4346 section 7.4
diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl
index 23a5c93452..8d19abfe1e 100644
--- a/lib/ssl/src/ssl_internal.hrl
+++ b/lib/ssl/src/ssl_internal.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -57,12 +57,15 @@
verify_fun, % fun(CertVerifyErrors) -> boolean()
fail_if_no_peer_cert, % boolean()
verify_client_once, % boolean()
+ %% fun(Extensions, State, Verify, AccError) -> {Extensions, State, AccError}
+ validate_extensions_fun,
depth, % integer()
certfile, % file()
keyfile, % file()
key, %
password, %
cacertfile, % file()
+ dhfile, % file()
ciphers, %
%% Local policy for the server if it want's to reuse the session
%% or not. Defaluts to allways returning true.
@@ -71,6 +74,7 @@
%% If false sessions will never be reused, if true they
%% will be reused if possible.
reuse_sessions, % boolean()
+ renegotiate_at,
debug %
}).
diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl
index 6b83c2ea46..0151426d43 100644
--- a/lib/ssl/src/ssl_manager.erl
+++ b/lib/ssl/src/ssl_manager.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -74,13 +74,11 @@ connection_init(TrustedcertsFile, Role) ->
call({connection_init, TrustedcertsFile, Role}).
cache_pem_file(File) ->
- case ets:lookup(ssl_file_to_ref,File) of
- [{_,_,Content}] ->
+ case ssl_certificate_db:lookup_cached_certs(File) of
+ [{_,Content}] ->
{ok, Content};
[] ->
- {ok, Db} = call({cache_pem, File}),
- [{_,_,Content}] = ets:lookup(Db,File),
- {ok, Content}
+ call({cache_pem, File})
end.
%%--------------------------------------------------------------------
@@ -170,13 +168,14 @@ handle_call({{connection_init, TrustedcertsFile, _Role}, Pid}, _From,
session_cache = Cache} = State) ->
erlang:monitor(process, Pid),
Result =
- case (catch ssl_certificate_db:add_trusted_certs(Pid,
- TrustedcertsFile,
- Db)) of
- {ok, Ref} ->
- {ok, Ref, Cache};
- Error ->
- {error, Error}
+ try
+ {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, TrustedcertsFile, Db),
+ {ok, Ref, Cache}
+ catch
+ _:{badmatch, Error} ->
+ {error, Error};
+ _E:_R ->
+ {error, {_R,erlang:get_stacktrace()}}
end,
{reply, Result, State};
@@ -198,7 +197,9 @@ handle_call({{cache_pem, File},Pid}, _, State = #state{certificate_db = Db}) ->
try ssl_certificate_db:cache_pem_file(Pid,File,Db) of
Result ->
{reply, Result, State}
- catch _:Reason ->
+ catch _:{badmatch, Reason} ->
+ {reply, Reason, State};
+ _:Reason ->
{reply, {error, Reason}, State}
end;
diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl
index 37a3d1b639..da48f049f6 100644
--- a/lib/ssl/src/ssl_record.erl
+++ b/lib/ssl/src/ssl_record.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -45,7 +45,7 @@
%% Encoding records
-export([encode_handshake/3, encode_alert_record/3,
- encode_change_cipher_spec/2, encode_data/3]).
+ encode_change_cipher_spec/2, encode_data/4]).
%% Decoding
-export([decode_cipher_text/2]).
@@ -474,19 +474,31 @@ split_bin(Bin, ChunkSize, Acc) ->
lists:reverse(Acc, [Bin])
end.
-encode_data(Frag, Version, ConnectionStates)
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt)
when byte_size(Frag) < (?MAX_PLAIN_TEXT_LENGTH - 2048) ->
- encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates);
-encode_data(Frag, Version, ConnectionStates) ->
+ case encode_plain_text(?APPLICATION_DATA,Version,Frag,ConnectionStates, RenegotiateAt) of
+ {renegotiate, Data} ->
+ {[], Data, ConnectionStates};
+ {Msg, CS} ->
+ {Msg, [], CS}
+ end;
+
+encode_data(Frag, Version, ConnectionStates, RenegotiateAt) when is_binary(Frag) ->
Data = split_bin(Frag, ?MAX_PLAIN_TEXT_LENGTH - 2048),
- {CS1, Acc} =
- lists:foldl(fun(B, {CS0, Acc}) ->
- {ET, CS1} =
- encode_plain_text(?APPLICATION_DATA,
- Version, B, CS0),
- {CS1, [ET | Acc]}
- end, {ConnectionStates, []}, Data),
- {lists:reverse(Acc), CS1}.
+ encode_data(Data, Version, ConnectionStates, RenegotiateAt);
+
+encode_data(Data, Version, ConnectionStates0, RenegotiateAt) when is_list(Data) ->
+ {ConnectionStates, EncodedMsg, NotEncdedData} =
+ lists:foldl(fun(B, {CS0, Encoded, Rest}) ->
+ case encode_plain_text(?APPLICATION_DATA,
+ Version, B, CS0, RenegotiateAt) of
+ {renegotiate, NotEnc} ->
+ {CS0, Encoded, [NotEnc | Rest]};
+ {Enc, CS1} ->
+ {CS1, [Enc | Encoded], Rest}
+ end
+ end, {ConnectionStates0, [], []}, Data),
+ {lists:reverse(EncodedMsg), lists:reverse(NotEncdedData), ConnectionStates}.
encode_handshake(Frag, Version, ConnectionStates) ->
encode_plain_text(?HANDSHAKE, Version, Frag, ConnectionStates).
@@ -499,6 +511,16 @@ encode_alert_record(#alert{level = Level, description = Description},
encode_change_cipher_spec(Version, ConnectionStates) ->
encode_plain_text(?CHANGE_CIPHER_SPEC, Version, <<1:8>>, ConnectionStates).
+encode_plain_text(Type, Version, Data, ConnectionStates, RenegotiateAt) ->
+ #connection_states{current_write =
+ #connection_state{sequence_number = Num}} = ConnectionStates,
+ case renegotiate(Num, RenegotiateAt) of
+ false ->
+ encode_plain_text(Type, Version, Data, ConnectionStates);
+ true ->
+ {renegotiate, Data}
+ end.
+
encode_plain_text(Type, Version, Data, ConnectionStates) ->
#connection_states{current_write=#connection_state{
compression_state=CompS0,
@@ -511,6 +533,11 @@ encode_plain_text(Type, Version, Data, ConnectionStates) ->
CTBin = encode_tls_cipher_text(Type, Version, CipherText),
{CTBin, ConnectionStates#connection_states{current_write = CS2}}.
+renegotiate(N, M) when N < M->
+ false;
+renegotiate(_,_) ->
+ true.
+
encode_tls_cipher_text(Type, {MajVer, MinVer}, Fragment) ->
Length = erlang:iolist_size(Fragment),
[<<?BYTE(Type), ?BYTE(MajVer), ?BYTE(MinVer), ?UINT16(Length)>>, Fragment].
@@ -529,9 +556,6 @@ cipher(Type, Version, Fragment, CS0) ->
CS2 = CS1#connection_state{cipher_state=CipherS1},
{Ciphered, CS2}.
-decipher(TLS=#ssl_tls{type = ?CHANGE_CIPHER_SPEC}, CS) ->
- %% These are never encrypted
- {TLS, CS};
decipher(TLS=#ssl_tls{type=Type, version=Version, fragment=Fragment}, CS0) ->
SP = CS0#connection_state.security_parameters,
BCA = SP#security_parameters.bulk_cipher_algorithm, % eller Cipher?
diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl
index 7370e0f0b3..362b7039d4 100644
--- a/lib/ssl/src/ssl_record.hrl
+++ b/lib/ssl/src/ssl_record.hrl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -63,6 +63,13 @@
sequence_number
}).
+-define(MAX_SEQENCE_NUMBER, 18446744073709552000). %% math:pow(2, 64) - 1 = 1.8446744073709552e19
+%% Sequence numbers can not wrap so when max is about to be reached we should renegotiate.
+%% We will renegotiate a little before so that there will be sequence numbers left
+%% for the rehandshake and a little data.
+-define(MARGIN, 100).
+-define(DEFAULT_RENEGOTIATE_AT, ?MAX_SEQENCE_NUMBER - ?MARGIN).
+
%% ConnectionEnd
-define(SERVER, 0).
-define(CLIENT, 1).
diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl
index ab29ac64df..df809ce275 100644
--- a/lib/ssl/src/ssl_ssl3.erl
+++ b/lib/ssl/src/ssl_ssl3.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -182,13 +182,13 @@ setup_keys(export, MasterSecret, ServerRandom, ClientRandom,
suites() ->
[
%% TODO: uncomment when supported
- %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
- %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
%%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this?
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index e0013c48ac..ce9a135168 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -1,19 +1,19 @@
%%
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% 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.
-%%
+%%
%% %CopyrightEnd%
%%
@@ -136,13 +136,13 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor},
suites() ->
[
%% TODO: uncomment when supported
- %% ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
- %% TODO: Funkar inte, borde: ?TLS_RSA_WITH_AES_256_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
+ %%?TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
+ ?TLS_RSA_WITH_AES_256_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
?TLS_RSA_WITH_3DES_EDE_CBC_SHA,
- %% ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
+ ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
%% ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
?TLS_RSA_WITH_AES_128_CBC_SHA,
%%?TLS_DHE_DSS_WITH_RC4_128_SHA, TODO: Support this?
diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile
new file mode 100644
index 0000000000..bd86120c98
--- /dev/null
+++ b/lib/ssl/test/Makefile
@@ -0,0 +1,137 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# 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.
+#
+# %CopyrightEnd%
+#
+
+#
+
+# SSL test suite Makefile
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../vsn.mk
+VSN=$(GS_VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+
+MODULES = \
+ ssl_test_lib \
+ ssl_basic_SUITE \
+ ssl_packet_SUITE \
+ ssl_payload_SUITE \
+ ssl_to_openssl_SUITE \
+ ssl_test_MACHINE \
+ old_ssl_active_SUITE \
+ old_ssl_active_once_SUITE \
+ old_ssl_passive_SUITE \
+ old_ssl_verify_SUITE \
+ old_ssl_peer_cert_SUITE \
+ old_ssl_misc_SUITE \
+ old_ssl_protocol_SUITE \
+ old_transport_accept_SUITE \
+ old_ssl_dist_SUITE \
+ make_certs
+
+
+ERL_FILES = $(MODULES:%=%.erl)
+
+HRL_FILES = ssl_test_MACHINE.hrl
+
+HRL_FILES_SRC = \
+ ssl_pkix.hrl \
+ ssl_alert.hrl \
+ ssl_handshake.hrl
+
+HRL_FILES_INC = \
+ OTP-PKIX.hrl
+
+HRL_FILES_NEEDED_IN_TEST = \
+ $(HRL_FILES_SRC:%=../src/%) \
+ $(HRL_FILES_INC:%=../include/%)
+
+TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
+
+INCLUDES = -I. -I$(ERL_TOP)/lib/test_server/include/
+
+DATADIRS = ssl_basic_SUITE_data
+
+EMAKEFILE=Emakefile
+MAKE_EMAKE = $(wildcard $(ERL_TOP)/make/make_emakefile)
+
+COVER_FILE = ssl.cover
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/ssl_test
+
+# ----------------------------------------------------
+# FLAGS
+# The path to the test_server ebin dir is needed when
+# running the target "targets".
+# ----------------------------------------------------
+ERL_COMPILE_FLAGS += -pa ../../../internal_tools/test_server/ebin \
+ $(INCLUDES)
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+
+tests debug opt: $(BUILDTARGET)
+
+targets: $(TARGET_FILES)
+
+.PHONY: emakebuild
+
+emakebuild: $(EMAKEFILE)
+
+$(EMAKEFILE):
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' | grep -v Warning > $(EMAKEFILE)
+ $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) $(MODULES) | grep -v Warning >> $(EMAKEFILE)
+
+clean:
+ rm -f $(EMAKEFILE)
+ rm -f $(TARGET_FILES)
+ rm -f core *~
+
+docs:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: opt
+
+release_tests_spec: opt
+ $(INSTALL_DIR) $(RELSYSDIR)
+ $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) $(RELSYSDIR)
+ $(INSTALL_DATA) ssl.spec $(RELSYSDIR)
+ chmod -f -R u+w $(RELSYSDIR)
+ @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -)
+
+release_docs_spec:
+
+# Dependencies
+
+$(TARGET_FILES): $(HRL_FILES)
diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl
new file mode 100644
index 0000000000..0cdf33c3e2
--- /dev/null
+++ b/lib/ssl/test/make_certs.erl
@@ -0,0 +1,288 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(make_certs).
+
+-export([all/2]).
+
+-record(dn, {commonName,
+ organizationalUnitName = "Erlang OTP",
+ organizationName = "Ericsson AB",
+ localityName = "Stockholm",
+ countryName = "SE",
+ emailAddress = "[email protected]"}).
+
+all(DataDir, PrivDir) ->
+ OpenSSLCmd = "openssl",
+ create_rnd(DataDir, PrivDir), % For all requests
+ rootCA(PrivDir, OpenSSLCmd, "erlangCA"),
+ intermediateCA(PrivDir, OpenSSLCmd, "otpCA", "erlangCA"),
+ endusers(PrivDir, OpenSSLCmd, "otpCA", ["client", "server"]),
+ collect_certs(PrivDir, ["erlangCA", "otpCA"], ["client", "server"]),
+ %% 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),
+ remove_rnd(PrivDir).
+
+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, OpenSSLCmd, Name) ->
+ create_ca_dir(Root, Name, ca_cnf(Name)),
+ DN = #dn{commonName = Name},
+ create_self_signed_cert(Root, OpenSSLCmd, Name, req_cnf(DN)),
+ ok.
+
+intermediateCA(Root, OpenSSLCmd, CA, ParentCA) ->
+ CA = "otpCA",
+ create_ca_dir(Root, CA, ca_cnf(CA)),
+ CARoot = filename:join([Root, CA]),
+ DN = #dn{commonName = CA},
+ CnfFile = filename:join([CARoot, "req.cnf"]),
+ file:write_file(CnfFile, req_cnf(DN)),
+ KeyFile = filename:join([CARoot, "private", "key.pem"]),
+ ReqFile = filename:join([CARoot, "req.pem"]),
+ create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
+ CertFile = filename:join([CARoot, "cert.pem"]),
+ sign_req(Root, OpenSSLCmd, ParentCA, "ca_cert", ReqFile, CertFile).
+
+endusers(Root, OpenSSLCmd, CA, Users) ->
+ lists:foreach(fun(User) -> enduser(Root, OpenSSLCmd, CA, User) end, Users).
+
+enduser(Root, OpenSSLCmd, CA, User) ->
+ UsrRoot = filename:join([Root, User]),
+ file:make_dir(UsrRoot),
+ CnfFile = filename:join([UsrRoot, "req.cnf"]),
+ DN = #dn{commonName = User},
+ file:write_file(CnfFile, req_cnf(DN)),
+ KeyFile = filename:join([UsrRoot, "key.pem"]),
+ ReqFile = filename:join([UsrRoot, "req.pem"]),
+ create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile),
+ CertFile = filename:join([UsrRoot, "cert.pem"]),
+ sign_req(Root, OpenSSLCmd, CA, "user_cert", ReqFile, CertFile).
+
+collect_certs(Root, CAs, Users) ->
+ Bins = lists:foldr(
+ fun(CA, Acc) ->
+ File = filename:join([Root, CA, "cert.pem"]),
+ {ok, Bin} = file:read_file(File),
+ [Bin, "\n" | Acc]
+ end, [], CAs),
+ lists:foreach(
+ fun(User) ->
+ File = filename:join([Root, User, "cacerts.pem"]),
+ file:write_file(File, Bins)
+ end, Users).
+
+create_self_signed_cert(Root, OpenSSLCmd, CAName, Cnf) ->
+ 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 = [OpenSSLCmd, " req"
+ " -new"
+ " -x509"
+ " -config ", CnfFile,
+ " -keyout ", KeyFile,
+ " -out ", CertFile],
+ Env = [{"ROOTDIR", Root}],
+ cmd(Cmd, Env).
+
+create_ca_dir(Root, CAName, Cnf) ->
+ CARoot = filename:join([Root, CAName]),
+ file:make_dir(CARoot),
+ create_dirs(CARoot, ["certs", "crl", "newcerts", "private"]),
+ create_rnd(Root, filename:join([CAName, "private"])),
+ create_files(CARoot, [{"serial", "01\n"},
+ {"index.txt", ""},
+ {"ca.cnf", Cnf}]).
+
+create_req(Root, OpenSSLCmd, CnfFile, KeyFile, ReqFile) ->
+ Cmd = [OpenSSLCmd, " req"
+ " -new"
+ " -config ", CnfFile,
+ " -keyout ", KeyFile,
+ " -out ", ReqFile],
+ Env = [{"ROOTDIR", Root}],
+ cmd(Cmd, Env).
+
+sign_req(Root, OpenSSLCmd, CA, CertType, ReqFile, CertFile) ->
+ CACnfFile = filename:join([Root, CA, "ca.cnf"]),
+ Cmd = [OpenSSLCmd, " ca"
+ " -batch"
+ " -notext"
+ " -config ", CACnfFile,
+ " -extensions ", CertType,
+ " -in ", ReqFile,
+ " -out ", CertFile],
+ Env = [{"ROOTDIR", 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).
+
+eval_cmd(Port) ->
+ receive
+ {Port, {data, _}} ->
+ eval_cmd(Port);
+ {Port, eof} ->
+ ok
+ end,
+ receive
+ {Port, {exit_status, Status}} when Status /= 0 ->
+ %% io:fwrite("exit status: ~w~n", [Status]),
+ exit({eval_cmd, Status})
+ after 0 ->
+ ok
+ end.
+
+%%
+%% Contents of configuration files
+%%
+
+req_cnf(DN) ->
+ ["# 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 = 1024\n"
+ "RANDFILE = $ROOTDIR/RAND\n"
+ "encrypt_key = no\n"
+ "default_md = sha1\n"
+ "#string_mask = pkix\n"
+ "x509_extensions = ca_ext\n"
+ "prompt = no\n"
+ "distinguished_name= name\n"
+ "\n"
+
+ "[name]\n"
+ "commonName = ", DN#dn.commonName, "\n"
+ "organizationalUnitName = ", DN#dn.organizationalUnitName, "\n"
+ "organizationName = ", DN#dn.organizationName, "\n"
+ "localityName = ", DN#dn.localityName, "\n"
+ "countryName = ", DN#dn.countryName, "\n"
+ "emailAddress = ", DN#dn.emailAddress, "\n"
+ "\n"
+
+ "[ca_ext]\n"
+ "basicConstraints = critical, CA:true\n"
+ "keyUsage = cRLSign, keyCertSign\n"
+ "subjectKeyIdentifier = hash\n"
+ "subjectAltName = email:copy\n"].
+
+
+ca_cnf(CA) ->
+ ["# Purpose: Configuration for CAs.\n"
+ "\n"
+ "ROOTDIR = $ENV::ROOTDIR\n"
+ "default_ca = ca\n"
+ "\n"
+
+ "[ca]\n"
+ "dir = $ROOTDIR/", CA, "\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"
+ "private_key = $dir/private/key.pem\n"
+ "RANDFILE = $dir/private/RAND\n"
+ "\n"
+ "x509_extensions = user_cert\n"
+ "default_days = 3600\n"
+ "default_md = sha1\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"
+
+ "[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"
+ "\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"].
diff --git a/lib/ssl/test/old_ssl_active_SUITE.erl b/lib/ssl/test/old_ssl_active_SUITE.erl
new file mode 100644
index 0000000000..010596f351
--- /dev/null
+++ b/lib/ssl/test/old_ssl_active_SUITE.erl
@@ -0,0 +1,387 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_ssl_active_SUITE).
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ config/1,
+ finish/1,
+ cinit_return_chkclose/1,
+ sinit_return_chkclose/1,
+ cinit_big_return_chkclose/1,
+ sinit_big_return_chkclose/1,
+ cinit_big_echo_chkclose/1,
+ cinit_huge_echo_chkclose/1,
+ sinit_big_echo_chkclose/1,
+ cinit_few_echo_chkclose/1,
+ cinit_many_echo_chkclose/1,
+ cinit_cnocert/1
+ ]).
+
+-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+-define(MANYCONNS, ssl_test_MACHINE:many_conns()).
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test of ssl.erl interface in active mode.";
+all(suite) ->
+ {conf,
+ config,
+ [cinit_return_chkclose,
+ sinit_return_chkclose,
+ cinit_big_return_chkclose,
+ sinit_big_return_chkclose,
+ cinit_big_echo_chkclose,
+ cinit_huge_echo_chkclose,
+ sinit_big_echo_chkclose,
+ cinit_few_echo_chkclose,
+ cinit_many_echo_chkclose,
+ cinit_cnocert],
+ finish}.
+
+config(doc) ->
+ "Want to se what Config contains, and record the number of available "
+ "file descriptors";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+ case os:type() of
+ {unix, _} ->
+ ?line io:format("Max fd value: ~s", [os:cmd("ulimit -n")]);
+ _ ->
+ ok
+ end,
+ %% XXX Also record: Erlang/SSL version, version of OpenSSL,
+ %% operating system, version of OTP, Erts, kernel and stdlib.
+
+ %% Check if SSL exists. If this case fails, all other cases are skipped
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config.
+
+finish(doc) ->
+ "This test case has no mission other than closing the conf case";
+finish(suite) ->
+ [];
+finish(Config) ->
+ Config.
+
+cinit_return_chkclose(doc) ->
+ "Client sends 1000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+cinit_return_chkclose(suite) ->
+ [];
+cinit_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_return_chkclose(doc) ->
+ "Server sends 1000 bytes to client, that receives them, sends them "
+ "back, and closes. Server waits for close. Both have certs.";
+sinit_return_chkclose(suite) ->
+ [];
+sinit_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, [{ssl_imp, old}|SsslOpts]},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, [{ssl_imp, old}|CsslOpts]},
+ {connect, {Host, LPort}},
+ {recv, DataSize}, {send, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_big_return_chkclose(doc) ->
+ "Client sends 50000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+cinit_big_return_chkclose(suite) ->
+ [];
+cinit_big_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_big_return_chkclose(doc) ->
+ "Server sends 50000 bytes to client, that receives them, sends them "
+ "back, and closes. Server waits for close. Both have certs.";
+sinit_big_return_chkclose(suite) ->
+ [];
+sinit_big_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {recv, DataSize}, {send, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_big_echo_chkclose(doc) ->
+ "Client sends 50000 bytes to server, that echoes them back "
+ "and closes. Client waits for close. Both have certs.";
+cinit_big_echo_chkclose(suite) ->
+ [];
+cinit_big_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_huge_echo_chkclose(doc) ->
+ "Client sends 500000 bytes to server, that echoes them back "
+ "and closes. Client waits for close. Both have certs.";
+cinit_huge_echo_chkclose(suite) ->
+ [];
+cinit_huge_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 500000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_big_echo_chkclose(doc) ->
+ "Server sends 50000 bytes to client, that echoes them back "
+ "and closes. Server waits for close. Both have certs.";
+sinit_big_echo_chkclose(suite) ->
+ [];
+sinit_big_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {echo, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+
+%% This case is repeated several times.
+
+cinit_few_echo_chkclose(X) -> cinit_many_echo_chkclose(X, 7).
+
+cinit_many_echo_chkclose(X) -> cinit_many_echo_chkclose(X, ?MANYCONNS).
+
+cinit_many_echo_chkclose(doc, _NConns) ->
+ "N client sends 10000 bytes to server, that echoes them back "
+ "and closes. Clients wait for close. All have certs.";
+cinit_many_echo_chkclose(suite, _NConns) ->
+ [];
+cinit_many_echo_chkclose(Config, NConns) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 10000, LPort = 3456,
+ Timeout = 80000,
+
+ io:format("~w connections", [NConns]),
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+
+cinit_cnocert(doc) ->
+ "Client sends 1000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Client has no cert, "
+ "but server has.";
+cinit_cnocert(suite) ->
+ [];
+cinit_cnocert(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3457,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {_CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+
diff --git a/lib/ssl/test/old_ssl_active_once_SUITE.erl b/lib/ssl/test/old_ssl_active_once_SUITE.erl
new file mode 100644
index 0000000000..6224b17aa7
--- /dev/null
+++ b/lib/ssl/test/old_ssl_active_once_SUITE.erl
@@ -0,0 +1,409 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2002-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_ssl_active_once_SUITE).
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ config/1,
+ finish/1,
+ server_accept_timeout/1,
+ cinit_return_chkclose/1,
+ sinit_return_chkclose/1,
+ cinit_big_return_chkclose/1,
+ sinit_big_return_chkclose/1,
+ cinit_big_echo_chkclose/1,
+ cinit_huge_echo_chkclose/1,
+ sinit_big_echo_chkclose/1,
+ cinit_few_echo_chkclose/1,
+ cinit_many_echo_chkclose/1,
+ cinit_cnocert/1
+ ]).
+
+-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+-define(MANYCONNS, ssl_test_MACHINE:many_conns()).
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test of ssl.erl interface in passive mode.";
+all(suite) ->
+ {conf,
+ config,
+ [server_accept_timeout,
+ cinit_return_chkclose,
+ sinit_return_chkclose,
+ cinit_big_return_chkclose,
+ sinit_big_return_chkclose,
+ cinit_big_echo_chkclose,
+ cinit_huge_echo_chkclose,
+ sinit_big_echo_chkclose,
+ cinit_few_echo_chkclose,
+ cinit_many_echo_chkclose,
+ cinit_cnocert],
+ finish}.
+
+config(doc) ->
+ "Want to se what Config contains.";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+
+ %% Check if SSL exists. If this case fails, all other cases are skipped
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config.
+
+finish(doc) ->
+ "This test case has no mission other than closing the conf case";
+finish(suite) ->
+ [];
+finish(Config) ->
+ Config.
+
+server_accept_timeout(doc) ->
+ "Server has one pending accept with timeout. Checks that return "
+ "value is {error, timeout}.";
+server_accept_timeout(suite) ->
+ [];
+server_accept_timeout(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ LPort = 3456,
+ Timeout = 40000, NConns = 1,
+ AccTimeout = 3000,
+
+ ?line {ok, {_, SsslOpts}} = mk_ssl_cert_opts(Config),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, AccTimeout},
+ accept_timeout],
+ ?line test_server_only(NConns, LCmds, ACmds, Timeout, ?MODULE,
+ Config).
+
+cinit_return_chkclose(doc) ->
+ "Client sends 1000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+cinit_return_chkclose(suite) ->
+ [];
+cinit_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_return_chkclose(doc) ->
+ "Server sends 1000 bytes to client, that receives them, sends them "
+ "back, and closes. Server waits for close. Both have certs.";
+sinit_return_chkclose(suite) ->
+ [];
+sinit_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {recv, DataSize}, {send, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_big_return_chkclose(doc) ->
+ "Client sends 50000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+cinit_big_return_chkclose(suite) ->
+ [];
+cinit_big_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ %% Set {active, false} so that accept is passive to begin with.
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {sockopts, [{active, once}]}, % {active, once} here.
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_big_return_chkclose(doc) ->
+ "Server sends 50000 bytes to client, that receives them, sends them "
+ "back, and closes. Server waits for close. Both have certs.";
+sinit_big_return_chkclose(suite) ->
+ [];
+sinit_big_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {recv, DataSize}, {send, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_big_echo_chkclose(doc) ->
+ "Client sends 50000 bytes to server, that echoes them back "
+ "and closes. Client waits for close. Both have certs.";
+cinit_big_echo_chkclose(suite) ->
+ [];
+cinit_big_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_huge_echo_chkclose(doc) ->
+ "Client sends 500000 bytes to server, that echoes them back "
+ "and closes. Client waits for close. Both have certs.";
+cinit_huge_echo_chkclose(suite) ->
+ [];
+cinit_huge_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 500000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_big_echo_chkclose(doc) ->
+ "Server sends 50000 bytes to client, that echoes them back "
+ "and closes. Server waits for close. Both have certs.";
+sinit_big_echo_chkclose(suite) ->
+ [];
+sinit_big_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {echo, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_few_echo_chkclose(X) -> cinit_many_echo_chkclose(X, 7).
+
+cinit_many_echo_chkclose(X) -> cinit_many_echo_chkclose(X, ?MANYCONNS).
+
+cinit_many_echo_chkclose(doc, _NConns) ->
+ "client send 10000 bytes to server, that echoes them back "
+ "and closes. Clients wait for close. All have certs.";
+cinit_many_echo_chkclose(suite, _NConns) ->
+ [];
+cinit_many_echo_chkclose(Config, NConns) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 10000, LPort = 3456,
+ Timeout = 80000,
+
+ io:format("~w connections", [NConns]),
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_cnocert(doc) ->
+ "Client sends 1000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Client has no cert, "
+ "but server has.";
+cinit_cnocert(suite) ->
+ [];
+cinit_cnocert(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3457,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {_CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, once}]},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+
diff --git a/lib/ssl/test/old_ssl_dist_SUITE.erl b/lib/ssl/test/old_ssl_dist_SUITE.erl
new file mode 100644
index 0000000000..56209c3530
--- /dev/null
+++ b/lib/ssl/test/old_ssl_dist_SUITE.erl
@@ -0,0 +1,595 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+
+%%%-------------------------------------------------------------------
+%%% File : ssl_dist_SUITE.erl
+%%% Author : Rickard Green
+%%% Description : Test that the Erlang distribution works over ssl.
+%%%
+%%% Created : 15 Nov 2007 by Rickard Green
+%%%-------------------------------------------------------------------
+-module(old_ssl_dist_SUITE).
+
+-include("test_server.hrl").
+
+-define(DEFAULT_TIMETRAP_SECS, 240).
+
+-define(AWAIT_SLL_NODE_UP_TIMEOUT, 30000).
+
+-export([all/1]).
+-export([init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2,
+ fin_per_testcase/2]).
+-export([cnct2tstsrvr/1]).
+
+-export([basic/1]).
+
+-record(node_handle, {connection_handler, socket, name, nodename}).
+
+all(doc) ->
+ [];
+all(suite) ->
+ [basic].
+
+init_per_suite(Config) ->
+ add_ssl_opts_config(Config).
+
+end_per_suite(Config) ->
+ Config.
+
+init_per_testcase(Case, Config) when list(Config) ->
+ Dog = ?t:timetrap(?t:seconds(?DEFAULT_TIMETRAP_SECS)),
+ [{watchdog, Dog},{testcase, Case}|Config].
+
+fin_per_testcase(_Case, Config) when list(Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Testcases %%
+%% %%
+
+basic(doc) ->
+ ["Test that two nodes can connect via ssl distribution"];
+basic(suite) ->
+ [];
+basic(Config) when is_list(Config) ->
+ ?line NH1 = start_ssl_node(Config),
+ ?line Node1 = NH1#node_handle.nodename,
+ ?line NH2 = start_ssl_node(Config),
+ ?line Node2 = NH2#node_handle.nodename,
+
+ ?line pong = apply_on_ssl_node(NH1, fun () -> net_adm:ping(Node2) end),
+
+ ?line [Node2] = apply_on_ssl_node(NH1, fun () -> nodes() end),
+ ?line [Node1] = apply_on_ssl_node(NH2, fun () -> nodes() end),
+
+ %% The test_server node has the same cookie as the ssl nodes
+ %% but it should not be able to communicate with the ssl nodes
+ %% via the erlang distribution.
+ ?line pang = net_adm:ping(Node1),
+ ?line pang = net_adm:ping(Node2),
+
+
+ %%
+ %% Check that we are able to communicate over the erlang
+ %% distribution between the ssl nodes.
+ %%
+ ?line Ref = make_ref(),
+ ?line spawn(fun () ->
+ apply_on_ssl_node(
+ NH1,
+ fun () ->
+ tstsrvr_format("Hi from ~p!~n",
+ [node()]),
+ send_to_tstcntrl({Ref, self()}),
+ receive
+ {From, ping} ->
+ From ! {self(), pong}
+ end
+ end)
+ end),
+ ?line receive
+ {Ref, SslPid} ->
+ ?line ok = apply_on_ssl_node(
+ NH2,
+ fun () ->
+ tstsrvr_format("Hi from ~p!~n",
+ [node()]),
+ SslPid ! {self(), ping},
+ receive
+ {SslPid, pong} ->
+ ok
+ end
+ end)
+ end,
+
+ ?line stop_ssl_node(NH1),
+ ?line stop_ssl_node(NH2),
+ ?line success(Config).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% %%
+%% Internal functions %%
+%% %%
+
+%%
+%% ssl_node side api
+%%
+
+tstsrvr_format(Fmt, ArgList) ->
+ send_to_tstsrvr({format, Fmt, ArgList}).
+
+send_to_tstcntrl(Message) ->
+ send_to_tstsrvr({message, Message}).
+
+
+%%
+%% test_server side api
+%%
+
+apply_on_ssl_node(Node, M, F, A) when atom(M), atom(F), list(A) ->
+ Ref = make_ref(),
+ send_to_ssl_node(Node, {apply, self(), Ref, M, F, A}),
+ receive
+ {Ref, Result} ->
+ Result
+ end.
+
+apply_on_ssl_node(Node, Fun) when is_function(Fun, 0) ->
+ Ref = make_ref(),
+ send_to_ssl_node(Node, {apply, self(), Ref, Fun}),
+ receive
+ {Ref, Result} ->
+ Result
+ end.
+
+stop_ssl_node(#node_handle{connection_handler = Handler,
+ socket = Socket,
+ name = Name}) ->
+ ?t:format("Trying to stop ssl node ~s.~n", [Name]),
+ Mon = erlang:monitor(process, Handler),
+ unlink(Handler),
+ case gen_tcp:send(Socket, term_to_binary(stop)) of
+ ok ->
+ receive
+ {'DOWN', Mon, process, Handler, Reason} ->
+ case Reason of
+ normal -> ok;
+ _ -> exit(Reason)
+ end
+ end;
+ Error ->
+ erlang:demonitor(Mon, [flush]),
+ exit(Error)
+ end.
+
+start_ssl_node(Config) ->
+ start_ssl_node(Config, "").
+
+start_ssl_node(Config, XArgs) ->
+ Name = mk_node_name(Config),
+ SSL = ?config(ssl_opts, Config),
+ SSLDistOpts = setup_dist_opts(Name, ?config(priv_dir, Config)),
+ start_ssl_node_raw(Name, SSL ++ " " ++ SSLDistOpts ++ XArgs).
+
+start_ssl_node_raw(Name, Args) ->
+ {ok, LSock} = gen_tcp:listen(0,
+ [binary, {packet, 4}, {active, false}]),
+ {ok, ListenPort} = inet:port(LSock),
+ CmdLine = mk_node_cmdline(ListenPort, Name, Args),
+ ?t:format("Attempting to start ssl node ~s: ~s~n", [Name, CmdLine]),
+ case open_port({spawn, CmdLine}, []) of
+ Port when port(Port) ->
+ unlink(Port),
+ erlang:port_close(Port),
+ case await_ssl_node_up(Name, LSock) of
+ #node_handle{} = NodeHandle ->
+ ?t:format("Ssl node ~s started.~n", [Name]),
+ NodeName = list_to_atom(Name ++ "@" ++ host_name()),
+ NodeHandle#node_handle{nodename = NodeName};
+ Error ->
+ exit({failed_to_start_node, Name, Error})
+ end;
+ Error ->
+ exit({failed_to_start_node, Name, Error})
+ end.
+
+%%
+%% command line creation
+%%
+
+host_name() ->
+ [$@ | Host] = lists:dropwhile(fun ($@) -> false; (_) -> true end,
+ atom_to_list(node())),
+ Host.
+
+mk_node_name(Config) ->
+ {A, B, C} = erlang:now(),
+ Case = ?config(testcase, Config),
+ atom_to_list(?MODULE)
+ ++ "_"
+ ++ atom_to_list(Case)
+ ++ "_"
+ ++ integer_to_list(A)
+ ++ "-"
+ ++ integer_to_list(B)
+ ++ "-"
+ ++ integer_to_list(C).
+
+mk_node_cmdline(ListenPort, Name, Args) ->
+ Static = "-detached -noinput",
+ Pa = filename:dirname(code:which(?MODULE)),
+ Prog = case catch init:get_argument(progname) of
+ {ok,[[P]]} -> P;
+ _ -> exit(no_progname_argument_found)
+ end,
+ NameSw = case net_kernel:longnames() of
+ false -> "-sname ";
+ _ -> "-name "
+ end,
+ {ok, Pwd} = file:get_cwd(),
+ Prog ++ " "
+ ++ Static ++ " "
+ ++ NameSw ++ " " ++ Name ++ " "
+ ++ "-pa " ++ Pa ++ " "
+ ++ "-run " ++ atom_to_list(?MODULE) ++ " cnct2tstsrvr "
+ ++ host_name() ++ " "
+ ++ integer_to_list(ListenPort) ++ " "
+ ++ Args ++ " "
+ ++ "-env ERL_CRASH_DUMP " ++ Pwd ++ "/erl_crash_dump." ++ Name ++ " "
+ ++ "-setcookie " ++ atom_to_list(erlang:get_cookie()).
+
+%%
+%% Connection handler test_server side
+%%
+
+await_ssl_node_up(Name, LSock) ->
+ case gen_tcp:accept(LSock, ?AWAIT_SLL_NODE_UP_TIMEOUT) of
+ timeout ->
+ gen_tcp:close(LSock),
+ ?t:format("Timeout waiting for ssl node ~s to come up~n",
+ [Name]),
+ timeout;
+ {ok, Socket} ->
+ gen_tcp:close(LSock),
+ case gen_tcp:recv(Socket, 0) of
+ {ok, Bin} ->
+ check_ssl_node_up(Socket, Name, Bin);
+ {error, closed} ->
+ gen_tcp:close(Socket),
+ exit({lost_connection_with_ssl_node_before_up, Name})
+ end;
+ {error, Error} ->
+ gen_tcp:close(LSock),
+ exit({accept_failed, Error})
+ end.
+
+check_ssl_node_up(Socket, Name, Bin) ->
+ case catch binary_to_term(Bin) of
+ {'EXIT', _} ->
+ gen_tcp:close(Socket),
+ exit({bad_data_received_from_ssl_node, Name, Bin});
+ {ssl_node_up, NodeName} ->
+ case list_to_atom(Name++"@"++host_name()) of
+ NodeName ->
+ Parent = self(),
+ Go = make_ref(),
+ %% Spawn connection handler on test server side
+ Pid = spawn_link(
+ fun () ->
+ receive Go -> ok end,
+ tstsrvr_con_loop(Name, Socket, Parent)
+ end),
+ ok = gen_tcp:controlling_process(Socket, Pid),
+ Pid ! Go,
+ #node_handle{connection_handler = Pid,
+ socket = Socket,
+ name = Name};
+ _ ->
+ exit({unexpected_ssl_node_connected, NodeName})
+ end;
+ Msg ->
+ exit({unexpected_msg_instead_of_ssl_node_up, Name, Msg})
+ end.
+
+send_to_ssl_node(#node_handle{connection_handler = Hndlr}, Term) ->
+ Hndlr ! {relay_to_ssl_node, term_to_binary(Term)},
+ ok.
+
+tstsrvr_con_loop(Name, Socket, Parent) ->
+ inet:setopts(Socket,[{active,once}]),
+ receive
+ {relay_to_ssl_node, Data} when is_binary(Data) ->
+ case gen_tcp:send(Socket, Data) of
+ ok ->
+ ok;
+ _Error ->
+ gen_tcp:close(Socket),
+ exit({failed_to_relay_data_to_ssl_node, Name, Data})
+ end;
+ {tcp, Socket, Bin} ->
+ case catch binary_to_term(Bin) of
+ {'EXIT', _} ->
+ gen_tcp:close(Socket),
+ exit({bad_data_received_from_ssl_node, Name, Bin});
+ {format, FmtStr, ArgList} ->
+ ?t:format(FmtStr, ArgList);
+ {message, Msg} ->
+ Parent ! Msg;
+ {apply_res, To, Ref, Res} ->
+ To ! {Ref, Res};
+ bye ->
+ ?t:format("Ssl node ~s stopped.~n", [Name]),
+ gen_tcp:close(Socket),
+ exit(normal);
+ Unknown ->
+ exit({unexpected_message_from_ssl_node, Name, Unknown})
+ end;
+ {tcp_closed, Socket} ->
+ gen_tcp:close(Socket),
+ exit({lost_connection_with_ssl_node, Name})
+ end,
+ tstsrvr_con_loop(Name, Socket, Parent).
+
+%%
+%% Connection handler ssl_node side
+%%
+
+% cnct2tstsrvr() is called via command line arg -run ...
+cnct2tstsrvr([Host, Port]) when list(Host), list(Port) ->
+ %% Spawn connection handler on ssl node side
+ ConnHandler
+ = spawn(fun () ->
+ case catch gen_tcp:connect(Host,
+ list_to_integer(Port),
+ [binary,
+ {packet, 4},
+ {active, false}]) of
+ {ok, Socket} ->
+ notify_ssl_node_up(Socket),
+ ets:new(test_server_info,
+ [set,
+ public,
+ named_table,
+ {keypos, 1}]),
+ ets:insert(test_server_info,
+ {test_server_handler, self()}),
+ ssl_node_con_loop(Socket);
+ _Error ->
+ halt("Failed to connect to test server")
+ end
+ end),
+ spawn(fun () ->
+ Mon = erlang:monitor(process, ConnHandler),
+ receive
+ {'DOWN', Mon, process, ConnHandler, Reason} ->
+ receive after 1000 -> ok end,
+ halt("test server connection handler terminated: "
+ ++
+ lists:flatten(io_lib:format("~p", [Reason])))
+ end
+ end).
+
+notify_ssl_node_up(Socket) ->
+ case catch gen_tcp:send(Socket,
+ term_to_binary({ssl_node_up, node()})) of
+ ok -> ok;
+ _ -> halt("Failed to notify test server that I'm up")
+ end.
+
+send_to_tstsrvr(Term) ->
+ case catch ets:lookup_element(test_server_info, test_server_handler, 2) of
+ Hndlr when pid(Hndlr) ->
+ Hndlr ! {relay_to_test_server, term_to_binary(Term)}, ok;
+ _ ->
+ receive after 200 -> ok end,
+ send_to_tstsrvr(Term)
+ end.
+
+ssl_node_con_loop(Socket) ->
+ inet:setopts(Socket,[{active,once}]),
+ receive
+ {relay_to_test_server, Data} when is_binary(Data) ->
+ case gen_tcp:send(Socket, Data) of
+ ok ->
+ ok;
+ _Error ->
+ gen_tcp:close(Socket),
+ halt("Failed to relay data to test server")
+ end;
+ {tcp, Socket, Bin} ->
+ case catch binary_to_term(Bin) of
+ {'EXIT', _} ->
+ gen_tcp:close(Socket),
+ halt("test server sent me bad data");
+ {apply, From, Ref, M, F, A} ->
+ spawn_link(
+ fun () ->
+ send_to_tstsrvr({apply_res,
+ From,
+ Ref,
+ (catch apply(M, F, A))})
+ end);
+ {apply, From, Ref, Fun} ->
+ spawn_link(fun () ->
+ send_to_tstsrvr({apply_res,
+ From,
+ Ref,
+ (catch Fun())})
+ end);
+ stop ->
+ gen_tcp:send(Socket, term_to_binary(bye)),
+ gen_tcp:close(Socket),
+ init:stop(),
+ receive after infinity -> ok end;
+ _Unknown ->
+ halt("test server sent me an unexpected message")
+ end;
+ {tcp_closed, Socket} ->
+ halt("Lost connection to test server")
+ end,
+ ssl_node_con_loop(Socket).
+
+%%
+%% Setup ssl dist info
+%%
+
+rand_bin(N) ->
+ rand_bin(N, []).
+
+rand_bin(0, Acc) ->
+ Acc;
+rand_bin(N, Acc) ->
+ rand_bin(N-1, [random:uniform(256)-1|Acc]).
+
+make_randfile(Dir) ->
+ {ok, IoDev} = file:open(filename:join([Dir, "RAND"]), [write]),
+ {A, B, C} = erlang:now(),
+ random:seed(A, B, C),
+ ok = file:write(IoDev, rand_bin(1024)),
+ file:close(IoDev).
+
+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).
+
+setup_dist_opts(Name, PrivDir) ->
+ NodeDir = filename:join([PrivDir, Name]),
+ RGenDir = filename:join([NodeDir, "rand_gen"]),
+ ok = file:make_dir(NodeDir),
+ ok = file:make_dir(RGenDir),
+ make_randfile(RGenDir),
+ make_certs:all(RGenDir, NodeDir),
+ SDir = filename:join([NodeDir, "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([NodeDir, "client"]),
+ CC = filename:join([CDir, "cert.pem"]),
+ CK = filename:join([CDir, "key.pem"]),
+ CKC = filename:join([CDir, "keycert.pem"]),
+ append_files([CK, CC], CKC),
+ "-proto_dist inet_ssl "
+ ++ "-ssl_dist_opt server_certfile " ++ SKC ++ " "
+ ++ "-ssl_dist_opt client_certfile " ++ CKC ++ " "
+.% ++ "-ssl_dist_opt verify 1 depth 1".
+
+%%
+%% Start scripts etc...
+%%
+
+add_ssl_opts_config(Config) ->
+ %%
+ %% Start with boot scripts if on an installed system; otherwise,
+ %% just point out ssl ebin with -pa.
+ %%
+ try
+ Dir = ?config(priv_dir, Config),
+ LibDir = code:lib_dir(),
+ Apps = application:which_applications(),
+ {value, {stdlib, _, STDL_VSN}} = lists:keysearch(stdlib, 1, Apps),
+ {value, {kernel, _, KRNL_VSN}} = lists:keysearch(kernel, 1, Apps),
+ StdlDir = filename:join([LibDir, "stdlib-" ++ STDL_VSN]),
+ KrnlDir = filename:join([LibDir, "kernel-" ++ KRNL_VSN]),
+ {ok, _} = file:read_file_info(StdlDir),
+ {ok, _} = file:read_file_info(KrnlDir),
+ SSL_VSN = case lists:keysearch(ssl, 1, Apps) of
+ {value, {ssl, _, VSN}} ->
+ VSN;
+ _ ->
+ application:start(ssl),
+ try
+ {value,
+ {ssl,
+ _,
+ VSN}} = lists:keysearch(ssl,
+ 1,
+ application:which_applications()),
+ VSN
+ after
+ application:stop(ssl)
+ end
+ end,
+ SslDir = filename:join([LibDir, "ssl-" ++ SSL_VSN]),
+ {ok, _} = file:read_file_info(SslDir),
+ %% We are using an installed otp system, create the boot script.
+ Script = filename:join(Dir, atom_to_list(?MODULE)),
+ {ok, RelFile} = file:open(Script ++ ".rel", [write]),
+ io:format(RelFile,
+ "{release, ~n"
+ " {\"SSL distribution test release\", \"~s\"},~n"
+ " {erts, \"~s\"},~n"
+ " [{kernel, \"~s\"},~n"
+ " {stdlib, \"~s\"},~n"
+ " {ssl, \"~s\"}]}.~n",
+ [case catch erlang:system_info(otp_release) of
+ {'EXIT', _} -> "R11B";
+ Rel -> Rel
+ end,
+ erlang:system_info(version),
+ KRNL_VSN,
+ STDL_VSN,
+ SSL_VSN]),
+ ok = file:close(RelFile),
+ ok = systools:make_script(Script, []),
+ [{ssl_opts, "-boot " ++ Script} | Config]
+ catch
+ _:_ ->
+ [{ssl_opts, "-pa " ++ filename:dirname(code:which(ssl))}
+ | add_comment_config(
+ "Bootscript wasn't used since the test wasn't run on an "
+ "installed OTP system.",
+ Config)]
+ end.
+
+%%
+%% Add common comments to config
+%%
+
+add_comment_config(Comment, []) ->
+ [{comment, Comment}];
+add_comment_config(Comment, [{comment, OldComment} | Cs]) ->
+ [{comment, Comment ++ " " ++ OldComment} | Cs];
+add_comment_config(Comment, [C|Cs]) ->
+ [C|add_comment_config(Comment, Cs)].
+
+%%
+%% Call when test case success
+%%
+
+success(Config) ->
+ case lists:keysearch(comment, 1, Config) of
+ {value, {comment, _} = Res} -> Res;
+ _ -> ok
+ end.
diff --git a/lib/ssl/test/old_ssl_misc_SUITE.erl b/lib/ssl/test/old_ssl_misc_SUITE.erl
new file mode 100644
index 0000000000..55d1b71025
--- /dev/null
+++ b/lib/ssl/test/old_ssl_misc_SUITE.erl
@@ -0,0 +1,105 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_ssl_misc_SUITE).
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ config/1,
+ finish/1,
+ seed/1,
+ app/1
+ ]).
+
+-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+-define(MANYCONNS, 5).
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test of misc in ssl.erl interface.";
+all(suite) ->
+ {conf,
+ config,
+ [seed, app],
+ finish
+ }.
+
+config(doc) ->
+ "Want to se what Config contains.";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+
+ %% Check if SSL exists. If this case fails, all other cases are skipped
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config.
+
+finish(doc) ->
+ "This test case has no mission other than closing the conf case";
+finish(suite) ->
+ [];
+finish(Config) ->
+ Config.
+
+seed(doc) ->
+ "Test that ssl:seed/1 works.";
+seed(suite) ->
+ [];
+seed(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {_, SsslOpts}} = mk_ssl_cert_opts(Config),
+
+ LCmds = [{seed, "tjosan"},
+ {sockopts, [{backlog, NConns}, {active, once}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ?line test_server_only(NConns, LCmds, [], Timeout, ?MODULE,
+ Config).
+
+app(doc) ->
+ "Test that the ssl app file is ok";
+app(suite) ->
+ [];
+app(Config) when list(Config) ->
+ ?line ok = test_server:app_test(ssl).
+
+
diff --git a/lib/ssl/test/old_ssl_passive_SUITE.erl b/lib/ssl/test/old_ssl_passive_SUITE.erl
new file mode 100644
index 0000000000..4cb8c1f0cd
--- /dev/null
+++ b/lib/ssl/test/old_ssl_passive_SUITE.erl
@@ -0,0 +1,374 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_ssl_passive_SUITE).
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ config/1,
+ finish/1,
+ server_accept_timeout/1,
+ cinit_return_chkclose/1,
+ sinit_return_chkclose/1,
+ cinit_big_return_chkclose/1,
+ sinit_big_return_chkclose/1,
+ cinit_big_echo_chkclose/1,
+ sinit_big_echo_chkclose/1,
+ cinit_few_echo_chkclose/1,
+ cinit_many_echo_chkclose/1,
+ cinit_cnocert/1
+ ]).
+
+-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+-define(MANYCONNS, ssl_test_MACHINE:many_conns()).
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test of ssl.erl interface in passive mode.";
+all(suite) ->
+ {conf,
+ config,
+ [server_accept_timeout,
+ cinit_return_chkclose,
+ sinit_return_chkclose,
+ cinit_big_return_chkclose,
+ sinit_big_return_chkclose,
+ cinit_big_echo_chkclose,
+ sinit_big_echo_chkclose,
+ cinit_few_echo_chkclose,
+ cinit_many_echo_chkclose,
+ cinit_cnocert],
+ finish}.
+
+config(doc) ->
+ "Want to se what Config contains.";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+
+ %% Check if SSL exists. If this case fails, all other cases are skipped
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config.
+
+finish(doc) ->
+ "This test case has no mission other than closing the conf case";
+finish(suite) ->
+ [];
+finish(Config) ->
+ Config.
+
+server_accept_timeout(doc) ->
+ "Server has one pending accept with timeout. Checks that return "
+ "value is {error, timeout}.";
+server_accept_timeout(suite) ->
+ [];
+server_accept_timeout(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ LPort = 3456,
+ Timeout = 40000, NConns = 1,
+ AccTimeout = 3000,
+
+ ?line {ok, {_, SsslOpts}} = mk_ssl_cert_opts(Config),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, AccTimeout},
+ accept_timeout],
+ ?line test_server_only(NConns, LCmds, ACmds, Timeout, ?MODULE, Config).
+
+cinit_return_chkclose(doc) ->
+ "Client sends 1000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+cinit_return_chkclose(suite) ->
+ [];
+cinit_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_return_chkclose(doc) ->
+ "Server sends 1000 bytes to client, that receives them, sends them "
+ "back, and closes. Server waits for close. Both have certs.";
+sinit_return_chkclose(suite) ->
+ [];
+sinit_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {recv, DataSize}, {send, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_big_return_chkclose(doc) ->
+ "Client sends 50000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+cinit_big_return_chkclose(suite) ->
+ [];
+cinit_big_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_big_return_chkclose(doc) ->
+ "Server sends 50000 bytes to client, that receives them, sends them "
+ "back, and closes. Server waits for close. Both have certs.";
+sinit_big_return_chkclose(suite) ->
+ [];
+sinit_big_return_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {recv, DataSize}, {send, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_big_echo_chkclose(doc) ->
+ "Client sends 50000 bytes to server, that echoes them back "
+ "and closes. Client waits for close. Both have certs.";
+cinit_big_echo_chkclose(suite) ->
+ [];
+cinit_big_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+sinit_big_echo_chkclose(doc) ->
+ "Server sends 50000 bytes to client, that echoes them back "
+ "and closes. Server waits for close. Both have certs.";
+sinit_big_echo_chkclose(suite) ->
+ [];
+sinit_big_echo_chkclose(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 50000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {echo, DataSize},
+ close],
+
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+
+cinit_few_echo_chkclose(X) -> cinit_many_echo_chkclose(X, 7).
+
+cinit_many_echo_chkclose(X) -> cinit_many_echo_chkclose(X, ?MANYCONNS).
+
+cinit_many_echo_chkclose(doc, _NConns) ->
+ "clients send 10000 bytes to server, that echoes them back "
+ "and closes. Clients wait for close. All have certs.";
+cinit_many_echo_chkclose(suite, _NConns) ->
+ [];
+cinit_many_echo_chkclose(Config, NConns) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 10000, LPort = 3456,
+ Timeout = 80000,
+
+ io:format("~w connections", [NConns]),
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {echo, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
+cinit_cnocert(doc) ->
+ "Client sends 1000 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Client has no cert, "
+ "but server has.";
+cinit_cnocert(suite) ->
+ [];
+cinit_cnocert(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3457,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {_CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}, {active, false}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize}, {send, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sockopts, [{active, false}]},
+ {connect, {Host, LPort}},
+ {send, DataSize}, {recv, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, ?MODULE,
+ Config).
+
diff --git a/lib/ssl/test/old_ssl_peer_cert_SUITE.erl b/lib/ssl/test/old_ssl_peer_cert_SUITE.erl
new file mode 100644
index 0000000000..f0b8db2607
--- /dev/null
+++ b/lib/ssl/test/old_ssl_peer_cert_SUITE.erl
@@ -0,0 +1,180 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_ssl_peer_cert_SUITE).
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ config/1,
+ finish/1,
+ cinit_plain/1,
+ cinit_both_verify/1,
+ cinit_cnocert/1
+ ]).
+
+-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test of ssl verification and peer certificate retrieval.";
+all(suite) ->
+ {conf,
+ config,
+ [cinit_plain,
+ cinit_both_verify,
+ cinit_cnocert],
+ finish}.
+
+config(doc) ->
+ "Want to se what Config contains.";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+
+ %% Check if SSL exists. If this case fails, all other cases are skipped
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config.
+
+finish(doc) ->
+ "This test case has no mission other than closing the conf case";
+finish(suite) ->
+ [];
+finish(Config) ->
+ Config.
+
+cinit_plain(doc) ->
+ "Server closes after accept, Client waits for close. Both have certs "
+ "but both use the defaults for verify and depth, but still tries "
+ "to retreive each others certificates.";
+cinit_plain(suite) ->
+ [];
+cinit_plain(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts, SsslOpts}} = mk_ssl_cert_opts(Config),
+
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ nopeercert,
+ {recv, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ peercert,
+ {send, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout,
+ ?MODULE, Config).
+
+cinit_both_verify(doc) ->
+ "Server closes after accept, Client waits for close. Both have certs "
+ "and both verify each other.";
+cinit_both_verify(suite) ->
+ [];
+cinit_both_verify(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts0, SsslOpts0}} = mk_ssl_cert_opts(Config),
+ ?line CsslOpts = [{verify, 2}, {depth, 2} | CsslOpts0],
+ ?line SsslOpts = [{verify, 2}, {depth, 3} | SsslOpts0],
+
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ peercert,
+ {recv, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ peercert,
+ {send, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout,
+ ?MODULE, Config).
+
+cinit_cnocert(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close.";
+cinit_cnocert(suite) ->
+ [];
+cinit_cnocert(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3457,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {_, SsslOpts0}} = mk_ssl_cert_opts(Config),
+ ?line SsslOpts = [{verify, 0}, {depth, 2} | SsslOpts0],
+
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {connect, {Host, LPort}},
+ peercert,
+ {send, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout,
+ ?MODULE, Config).
+
+
diff --git a/lib/ssl/test/old_ssl_protocol_SUITE.erl b/lib/ssl/test/old_ssl_protocol_SUITE.erl
new file mode 100644
index 0000000000..7bde5d6749
--- /dev/null
+++ b/lib/ssl/test/old_ssl_protocol_SUITE.erl
@@ -0,0 +1,169 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_ssl_protocol_SUITE).
+
+-export([all/1, init_per_testcase/2, fin_per_testcase/2, config/1,
+ finish/1, sslv2/1, sslv3/1, tlsv1/1, sslv2_sslv3/1,
+ sslv2_tlsv1/1, sslv3_tlsv1/1, sslv2_sslv3_tlsv1/1]).
+
+-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = test_server:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test of configuration protocol_version.";
+all(suite) ->
+ {conf,
+ config,
+ [sslv2, sslv3, tlsv1, sslv2_sslv3, sslv2_tlsv1, sslv3_tlsv1,
+ sslv2_sslv3_tlsv1],
+ finish}.
+
+config(doc) ->
+ "Want to se what Config contains.";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+
+ %% Check if SSL exists. If this case fails, all other cases are skipped
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config.
+
+finish(doc) ->
+ "This test case has no other purpose than closing the conf case.";
+finish(suite) ->
+ [];
+finish(Config) ->
+ Config.
+
+%%%%%
+
+sslv2(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close. "
+ "Client and server choose SSLv2.";
+sslv2(suite) ->
+ [];
+sslv2(Config) when list(Config) ->
+ do_run_test(Config, [sslv2]).
+
+sslv3(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close. "
+ "Client and server choose SSLv3.";
+sslv3(suite) ->
+ [];
+sslv3(Config) when list(Config) ->
+ do_run_test(Config, [sslv3]).
+
+tlsv1(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close. "
+ "Client and server choose TLSv1.";
+tlsv1(suite) ->
+ [];
+tlsv1(Config) when list(Config) ->
+ do_run_test(Config, [tlsv1]).
+
+sslv2_sslv3(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close. "
+ "Client and server choose between SSLv2 and SSLv3.";
+sslv2_sslv3(suite) ->
+ [];
+sslv2_sslv3(Config) when list(Config) ->
+ do_run_test(Config, [sslv2, sslv3]).
+
+sslv2_tlsv1(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close. "
+ "Client and server choose between SSLv2 and TLSv1.";
+sslv2_tlsv1(suite) ->
+ [];
+sslv2_tlsv1(Config) when list(Config) ->
+ do_run_test(Config, [sslv2, tlsv1]).
+
+sslv3_tlsv1(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close. "
+ "Client and server choose between SSLv3 and TLSv1.";
+sslv3_tlsv1(suite) ->
+ [];
+sslv3_tlsv1(Config) when list(Config) ->
+ do_run_test(Config, [sslv3, tlsv1]).
+
+sslv2_sslv3_tlsv1(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close. "
+ "Client and server choose between SSLv2, SSLv3, and TLSv1.";
+sslv2_sslv3_tlsv1(suite) ->
+ [];
+sslv2_sslv3_tlsv1(Config) when list(Config) ->
+ do_run_test(Config, [sslv2, sslv3, tlsv1]).
+
+%%%%
+
+do_run_test(Config0, Protocols) ->
+ process_flag(trap_exit, true),
+ LPort = 3456,
+ Timeout = 40000, NConns = 1,
+ DataSize = 10,
+
+ ?line {ok, {_, SsslOpts0}} = mk_ssl_cert_opts(Config0),
+ ?line SsslOpts = [{verify, 0}, {depth, 2} | SsslOpts0],
+
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ connection_info,
+ {recv, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {connect, {Host, LPort}},
+ connection_info,
+ {send, DataSize},
+ await_close],
+ Config1 = [{env, [{protocol_version, Protocols}]} | Config0],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout,
+ ?MODULE, Config1).
+
+
diff --git a/lib/ssl/test/old_ssl_verify_SUITE.erl b/lib/ssl/test/old_ssl_verify_SUITE.erl
new file mode 100644
index 0000000000..5db964526f
--- /dev/null
+++ b/lib/ssl/test/old_ssl_verify_SUITE.erl
@@ -0,0 +1,141 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1999-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_ssl_verify_SUITE).
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ config/1,
+ finish/1,
+ cinit_both_verify/1,
+ cinit_cnocert/1
+ ]).
+
+-import(ssl_test_MACHINE, [mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = ssl_test_lib:timetrap(?DEFAULT_TIMEOUT),
+ [{watchdog, WatchDog}| Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test of ssl.erl interface in active mode.";
+all(suite) ->
+ {conf,
+ config,
+ [cinit_both_verify,
+ cinit_cnocert],
+ finish}.
+
+config(doc) ->
+ "Want to se what Config contains.";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+
+ %% Check if SSL exists. If this case fails, all other cases are skipped
+ case ssl:start() of
+ ok -> ssl:stop();
+ {error, {already_started, _}} -> ssl:stop();
+ Error -> ?t:fail({failed_starting_ssl,Error})
+ end,
+ Config.
+
+finish(doc) ->
+ "This test case has no mission other than closing the conf case";
+finish(suite) ->
+ [];
+finish(Config) ->
+ Config.
+
+cinit_both_verify(doc) ->
+ "Server closes after accept, Client waits for close. Both have certs "
+ "and both verify each other.";
+cinit_both_verify(suite) ->
+ [];
+cinit_both_verify(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3456,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {CsslOpts0, SsslOpts0}} = mk_ssl_cert_opts(Config),
+ ?line CsslOpts = [{verify, 2}, {depth, 2} | CsslOpts0],
+ ?line SsslOpts = [{verify, 2}, {depth, 3} | SsslOpts0],
+
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {sslopts, CsslOpts},
+ {connect, {Host, LPort}},
+ {send, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout,
+ ?MODULE, Config).
+
+cinit_cnocert(doc) ->
+ "Client has no cert. Nor the client, nor the server is verifying its "
+ "peer. Server closes, client waits for close.";
+cinit_cnocert(suite) ->
+ [];
+cinit_cnocert(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ DataSize = 1000, LPort = 3457,
+ Timeout = 40000, NConns = 1,
+
+ ?line {ok, {_, SsslOpts0}} = mk_ssl_cert_opts(Config),
+ ?line SsslOpts = [{verify, 0}, {depth, 2} | SsslOpts0],
+
+ ?line {ok, Host} = inet:gethostname(),
+
+ LCmds = [{sockopts, [{backlog, NConns}]},
+ {sslopts, SsslOpts},
+ {listen, LPort},
+ wait_sync,
+ lclose],
+ ACmds = [{timeout, Timeout},
+ accept,
+ {recv, DataSize},
+ close],
+ CCmds = [{timeout, Timeout},
+ {connect, {Host, LPort}},
+ {send, DataSize},
+ await_close],
+ ?line test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout,
+ ?MODULE, Config).
+
+
diff --git a/lib/ssl/test/old_transport_accept_SUITE.erl b/lib/ssl/test/old_transport_accept_SUITE.erl
new file mode 100644
index 0000000000..4bb09cee19
--- /dev/null
+++ b/lib/ssl/test/old_transport_accept_SUITE.erl
@@ -0,0 +1,238 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(old_transport_accept_SUITE).
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+
+%% Default timetrap timeout (set in init_per_testcase).
+-define(default_timeout, ?t:minutes(1)).
+-define(application, ssh).
+
+-export([all/1,
+ init_per_testcase/2,
+ fin_per_testcase/2,
+ config/1,
+ echo_once/1,
+ echo_twice/1,
+ close_before_ssl_accept/1,
+ server/5,
+ tolerant_server/5,
+ client/5
+ ]).
+
+init_per_testcase(_Case, Config) ->
+ WatchDog = ssl_test_lib:timetrap(?default_timeout),
+ [{watchdog, WatchDog}, {protomod, gen_tcp}, {serialize_accept, true}|
+ Config].
+
+fin_per_testcase(_Case, Config) ->
+ WatchDog = ?config(watchdog, Config),
+ test_server:timetrap_cancel(WatchDog).
+
+all(doc) ->
+ "Test transport_accept and ssl_accept";
+all(suite) ->
+ [config, echo_once, echo_twice, close_before_ssl_accept].
+
+config(doc) ->
+ "Want to se what Config contains.";
+config(suite) ->
+ [];
+config(Config) ->
+ io:format("Config: ~p~n", [Config]),
+ ok.
+
+echo_once(doc) ->
+ "Client sends 256 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+echo_once(suite) ->
+ [];
+echo_once(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ LPort = 3456,
+ {ok, Host} = inet:gethostname(),
+ {ok, {COpts, SOpts}} = ssl_test_MACHINE:mk_ssl_cert_opts(Config),
+ N = 1,
+ Msg = lists:seq(0, 255),
+ Self = self(),
+ Params = "-pa " ++ filename:dirname(code:which(?MODULE)),
+ Node = start_node(server, Params),
+ CNode = start_node(client, Params),
+ Server = spawn_link(Node, ?MODULE, server, [Self, LPort, SOpts, Msg, N]),
+ Client = spawn_link(Node, ?MODULE, client, [Host, LPort, COpts, Msg, N]),
+ ok = receive
+ {Server, listening} ->
+ Client ! {Server, listening},
+ ok;
+ E ->
+ io:format("bad receive (1) ~p\n", [E]),
+ E
+ end,
+ receive
+ {Server, done} ->
+ ok
+ end,
+ test_server:stop_node(Node),
+ test_server:stop_node(CNode).
+
+close_before_ssl_accept(doc) ->
+ "Client sends 256 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+close_before_ssl_accept(suite) ->
+ [];
+close_before_ssl_accept(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ LPort = 3456,
+ {ok, Host} = inet:gethostname(),
+ {ok, {COpts, SOpts}} = ssl_test_MACHINE:mk_ssl_cert_opts(Config),
+ Msg = lists:seq(0, 255),
+ Self = self(),
+ Params = "-pa " ++ filename:dirname(code:which(?MODULE)),
+ Node = start_node(server, Params),
+ CNode = start_node(client, Params),
+ Server = spawn_link(Node, ?MODULE, tolerant_server,
+ [Self, LPort, SOpts, Msg, 2]),
+ Client = spawn_link(Node, ?MODULE, client,
+ [Host, LPort, COpts, Msg, 1]),
+ ok = receive
+ {Server, listening} ->
+ {ok, S} = gen_tcp:connect(Host, LPort, []),
+ gen_tcp:close(S),
+ Client ! {Server, listening},
+ ok;
+ E ->
+ io:format("bad receive (1) ~p\n", [E]),
+ E
+ end,
+ receive
+ {Server, done} ->
+ ok
+ end,
+ test_server:stop_node(Node),
+ test_server:stop_node(CNode).
+
+client(Host, LPort, COpts, Msg, N) ->
+ ok = receive
+ {_Server, listening} ->
+ ok;
+ E ->
+ io:format("bad receive (2) ~p\n", [E]),
+ E
+ end,
+ Opts = COpts ++ [{packet, raw}, {active, false}],
+ app(),
+ lists:foreach(fun(_) ->
+ {ok, S} = ssl:connect(Host, LPort, Opts),
+ ssl:send(S, Msg),
+ {ok, Msg} = ssl:recv(S, length(Msg)),
+ ssl:close(S)
+ end, lists:seq(1, N)).
+
+echo_twice(doc) ->
+ "Two clients sends 256 bytes to server, that receives them, sends them "
+ "back, and closes. Client waits for close. Both have certs.";
+echo_twice(suite) ->
+ [];
+echo_twice(Config) when list(Config) ->
+ process_flag(trap_exit, true),
+ LPort = 3456,
+ {ok, Host} = inet:gethostname(),
+ {ok, {COpts, SOpts}} = ssl_test_MACHINE:mk_ssl_cert_opts(Config),
+ N = 2,
+ Msg = lists:seq(0, 255),
+ Self = self(),
+ Params = "-pa " ++ filename:dirname(code:which(?MODULE)),
+ Node = start_node(server, Params),
+ CNode = start_node(client, Params),
+ Server = spawn_link(Node, ?MODULE, server,
+ [Self, LPort, SOpts, Msg, N]),
+ Client = spawn_link(Node, ?MODULE, client,
+ [Host, LPort, COpts, Msg, N]),
+ ok = receive
+ {Server, listening} ->
+ Client ! {Server, listening},
+ ok;
+ E ->
+ io:format("bad receive (3) ~p\n", [E]),
+ E
+ end,
+ receive
+ {Server, done} ->
+ ok
+ end,
+ test_server:stop_node(Node),
+ test_server:stop_node(CNode).
+
+server(Client, Port, SOpts, Msg, N) ->
+ app(),
+ process_flag(trap_exit, true),
+ Opts = SOpts ++ [{packet, raw}, {active, false}],
+ {ok, LSock} = ssl:listen(Port, Opts),
+ Client ! {self(), listening},
+ server_loop(Client, LSock, Msg, N).
+
+server_loop(Client, _, _, 0) ->
+ Client ! {self(), done};
+server_loop(Client, LSock, Msg, N) ->
+ {ok, S} = ssl:transport_accept(LSock),
+ ok = ssl:ssl_accept(S),
+ %% P = ssl:controlling_process(S, Proxy),
+ {ok, Msg} = ssl:recv(S, length(Msg)),
+ ok = ssl:send(S, Msg),
+ ok = ssl:close(S),
+ server_loop(Client, LSock, Msg, N-1).
+
+tolerant_server(Client, Port, SOpts, Msg, N) ->
+ app(),
+ process_flag(trap_exit, true),
+ Opts = SOpts ++ [{packet, raw}, {active, false}],
+ {ok, LSock} = ssl:listen(Port, Opts),
+ Client ! {self(), listening},
+ tolerant_server_loop(Client, LSock, Msg, N).
+
+tolerant_server_loop(Client, _, _, 0) ->
+ Client ! {self(), done};
+tolerant_server_loop(Client, LSock, Msg, N) ->
+ {ok, S} = ssl:transport_accept(LSock),
+ case ssl:ssl_accept(S) of
+ ok ->
+ %% P = ssl:controlling_process(S, Proxy),
+ {ok, Msg} = ssl:recv(S, length(Msg)),
+ ok = ssl:send(S, Msg),
+ ok = ssl:close(S);
+ E ->
+ io:format("ssl_accept error: ~p\n", [E])
+ end,
+ tolerant_server_loop(Client, LSock, Msg, N-1).
+
+app() ->
+ case application:get_application(ssl) of
+ undefined ->
+ application:start(ssl);
+ _ ->
+ ok
+ end.
+
+start_node(Kind, Params) ->
+ S = atom_to_list(?MODULE)++"_" ++ atom_to_list(Kind),
+ {ok, Node} = test_server:start_node(list_to_atom(S), slave, [{args, Params}]),
+ Node.
+
diff --git a/lib/ssl/test/ssl.cover b/lib/ssl/test/ssl.cover
new file mode 100644
index 0000000000..138bf96b9d
--- /dev/null
+++ b/lib/ssl/test/ssl.cover
@@ -0,0 +1,7 @@
+{exclude, [ssl_pkix_oid,
+ 'PKIX1Algorithms88',
+ 'PKIX1Explicit88',
+ 'PKIX1Implicit88',
+ 'PKIXAttributeCertificate',
+ 'SSL-PKIX']}.
+
diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec
new file mode 100644
index 0000000000..6ef4fb73db
--- /dev/null
+++ b/lib/ssl/test/ssl.spec
@@ -0,0 +1 @@
+{topcase, {dir, "../ssl_test"}}.
diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl
new file mode 100644
index 0000000000..7f33efd7e1
--- /dev/null
+++ b/lib/ssl/test/ssl_basic_SUITE.erl
@@ -0,0 +1,2538 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2007-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_basic_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+-include_lib("public_key/include/public_key.hrl").
+
+-define('24H_in_sec', 86400).
+-define(TIMEOUT, 60000).
+-define(EXPIRE, 10).
+-define(SLEEP, 500).
+
+
+-behaviour(ssl_session_cache_api).
+
+%% For the session cache tests
+-export([init/0, terminate/1, lookup/2, update/3,
+ delete/2, foldl/3, select_session/2]).
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initialization 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) ->
+ crypto:start(),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ test_server:format("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(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) ->
+ ssl:stop(),
+ crypto:stop().
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, 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: 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.
+%% Description: Initialization before each test case
+%%--------------------------------------------------------------------
+init_per_testcase(session_cache_process_list, Config) ->
+ init_customized_session_cache(Config);
+
+init_per_testcase(session_cache_process_mnesia, Config) ->
+ mnesia:start(),
+ init_customized_session_cache(Config);
+
+init_per_testcase(reuse_session_expired, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_lifetime, ?EXPIRE),
+ ssl:start(),
+ [{watchdog, Dog} | Config];
+
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = test_server:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+init_customized_session_cache(Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = test_server:timetrap(?TIMEOUT),
+ ssl:stop(),
+ application:load(ssl),
+ application:set_env(ssl, session_cb, ?MODULE),
+ ssl:start(),
+ [{watchdog, Dog} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, 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(session_cache_process_list, Config) ->
+ application:unset_env(ssl, session_cb),
+ end_per_testcase(default_action, Config);
+end_per_testcase(session_cache_process_mnesia, Config) ->
+ application:unset_env(ssl, session_cb),
+ mnesia:stop(),
+ end_per_testcase(default_action, Config);
+end_per_testcase(reuse_session_expired, Config) ->
+ application:unset_env(ssl, session_lifetime),
+ end_per_testcase(default_action, Config);
+end_per_testcase(_TestCase, Config) ->
+ Dog = ?config(watchdog, Config),
+ case Dog of
+ undefined ->
+ ok;
+ _ ->
+ test_server:timetrap_cancel(Dog)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> TestCases
+%% Clause - atom() - suite | doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%% Description: Returns a list of all test cases in this test suite
+%%--------------------------------------------------------------------
+all(doc) ->
+ ["Test the basic ssl functionality"];
+
+all(suite) ->
+ [app, connection_info, controlling_process, controller_dies,
+ peercert, connect_dist,
+ peername, sockname, socket_options, misc_ssl_options, versions, cipher_suites,
+ upgrade, upgrade_with_timeout, tcp_connect,
+ ipv6, ekeyfile, ecertfile, ecacertfile, eoptions, shutdown,
+ shutdown_write, shutdown_both, shutdown_error, ciphers,
+ send_close, close_transport_accept, dh_params,
+ server_verify_peer_passive,
+ server_verify_peer_active, server_verify_peer_active_once,
+ server_verify_none_passive, server_verify_none_active,
+ server_verify_none_active_once, server_verify_no_cacerts,
+ server_require_peer_cert_ok, server_require_peer_cert_fail,
+ server_verify_client_once_passive,
+ server_verify_client_once_active,
+ server_verify_client_once_active_once,
+ client_verify_none_passive,
+ client_verify_none_active, client_verify_none_active_once
+ %%, session_cache_process_list, session_cache_process_mnesia
+ ,reuse_session, reuse_session_expired, server_does_not_want_to_reuse_session,
+ client_renegotiate, server_renegotiate,
+ client_no_wrap_sequence_number, server_no_wrap_sequence_number,
+ extended_key_usage, validate_extensions_fun
+ ].
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+app(doc) ->
+ "Test that the ssl app file is ok";
+app(suite) ->
+ [];
+app(Config) when is_list(Config) ->
+ ok = test_server:app_test(ssl).
+
+connection_info(doc) ->
+ ["Test the API function ssl:connection_info/1"];
+connection_info(suite) ->
+ [];
+connection_info(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, ServerOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options,
+ [{ciphers,[{rsa,rc4_128,sha,no_export}]} |
+ ClientOpts]}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ ServerMsg = ClientMsg = {ok, {Version, {rsa,rc4_128,sha,no_export}}},
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+connection_info_result(Socket) ->
+ ssl:connection_info(Socket).
+
+%%--------------------------------------------------------------------
+
+controlling_process(doc) ->
+ ["Test API function controlling_process/2"];
+
+controlling_process(suite) ->
+ [];
+
+controlling_process(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ClientMsg = "Hello server",
+ ServerMsg = "Hello client",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ controlling_process_result, [self(),
+ ServerMsg]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ controlling_process_result, [self(),
+ ClientMsg]}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ receive
+ {ssl, _, ServerMsg} ->
+ receive
+ {ssl, _, ClientMsg} ->
+ ok
+ end;
+ {ssl, _, ClientMsg} ->
+ receive
+ {ssl, _, ServerMsg} ->
+ ok
+ end;
+ Unexpected ->
+ test_server:fail(Unexpected)
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+controlling_process_result(Socket, Pid, Msg) ->
+ ok = ssl:controlling_process(Socket, Pid),
+ %% Make sure other side has evaluated controlling_process
+ %% before message is sent
+ test_server:sleep(?SLEEP),
+ ssl:send(Socket, Msg),
+ no_result_msg.
+
+
+controller_dies(doc) ->
+ ["Test that the socket is closed after controlling process dies"];
+controller_dies(suite) -> [];
+controller_dies(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ ClientMsg = "Hello server",
+ ServerMsg = "Hello client",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ controller_dies_result, [self(),
+ ServerMsg]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ controller_dies_result, [self(),
+ ClientMsg]}},
+ {options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]),
+ test_server:sleep(?SLEEP), %% so that they are connected
+
+ process_flag(trap_exit, true),
+
+ %% Test that clients die
+ exit(Client, killed),
+ get_close(Client, ?LINE),
+
+ %% Test that clients die when process disappear
+ Server ! listen,
+ Tester = self(),
+ Connect = fun(Pid) ->
+ {ok, Socket} = ssl:connect(Hostname, Port,
+ [{reuseaddr,true},{ssl_imp,new}]),
+ Pid ! {self(), connected, Socket},
+ receive die_nice -> normal end
+ end,
+ Client2 = spawn_link(fun() -> Connect(Tester) end),
+ receive {Client2, connected, _Socket} -> Client2 ! die_nice end,
+
+ get_close(Client2, ?LINE),
+
+ %% Test that clients die when the controlling process have changed
+ Server ! listen,
+
+ Client3 = spawn_link(fun() -> Connect(Tester) end),
+ Controller = spawn_link(fun() -> receive die_nice -> normal end end),
+ receive
+ {Client3, connected, Socket} ->
+ ok = ssl:controlling_process(Socket, Controller),
+ Client3 ! die_nice
+ end,
+
+ test_server:format("Wating on exit ~p~n",[Client3]),
+ receive {'EXIT', Client3, normal} -> ok end,
+
+ receive %% Client3 is dead but that doesn't matter, socket should not be closed.
+ Unexpected ->
+ test_server:format("Unexpected ~p~n",[Unexpected]),
+ test_server:fail({line, ?LINE-1})
+ after 1000 ->
+ ok
+ end,
+ Controller ! die_nice,
+ get_close(Controller, ?LINE),
+
+ %% Test that servers die
+ Server ! listen,
+ LastClient = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ controller_dies_result, [self(),
+ ClientMsg]}},
+ {options, [{reuseaddr,true}|ClientOpts]}]),
+ test_server:sleep(?SLEEP), %% so that they are connected
+
+ exit(Server, killed),
+ get_close(Server, ?LINE),
+ process_flag(trap_exit, false),
+ ssl_test_lib:close(LastClient).
+
+controller_dies_result(_Socket, _Pid, _Msg) ->
+ receive Result -> Result end.
+
+get_close(Pid, Where) ->
+ receive
+ {'EXIT', Pid, _Reason} ->
+ receive
+ {_, {ssl_closed, Socket}} ->
+ test_server:format("Socket closed ~p~n",[Socket]);
+ Unexpected ->
+ test_server:format("Unexpected ~p~n",[Unexpected]),
+ test_server:fail({line, ?LINE-1})
+ after 5000 ->
+ test_server:fail({timeout, {line, ?LINE, Where}})
+ end;
+ Unexpected ->
+ test_server:format("Unexpected ~p~n",[Unexpected]),
+ test_server:fail({line, ?LINE-1})
+ after 5000 ->
+ test_server:fail({timeout, {line, ?LINE, Where}})
+ end.
+
+%%--------------------------------------------------------------------
+peercert(doc) ->
+ [""];
+
+peercert(suite) ->
+ [];
+
+peercert(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, peercert_result, []}},
+ {options, ClientOpts}]),
+
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ {ok, [{cert, BinCert, _}]} = public_key:pem_to_der(CertFile),
+ {ok, ErlCert} = public_key:pkix_decode_cert(BinCert, otp),
+
+ ServerMsg = {{error, no_peercert}, {error, no_peercert}},
+ ClientMsg = {{ok, BinCert}, {ok, ErlCert}},
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+peercert_result(Socket) ->
+ Result1 = ssl:peercert(Socket),
+ Result2 = ssl:peercert(Socket, [ssl]),
+ {Result1, Result2}.
+
+%%--------------------------------------------------------------------
+connect_dist(doc) ->
+ ["Test a simple connect as is used by distribution"];
+
+connect_dist(suite) ->
+ [];
+
+connect_dist(Config) when is_list(Config) ->
+ ClientOpts0 = ?config(client_kc_opts, Config),
+ ClientOpts = [{ssl_imp, new},{active, false}, {packet,4}|ClientOpts0],
+ ServerOpts0 = ?config(server_kc_opts, Config),
+ ServerOpts = [{ssl_imp, new},{active, false}, {packet,4}|ServerOpts0],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connect_dist_s, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connect_dist_c, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+connect_dist_s(S) ->
+ Msg = term_to_binary({erlang,term}),
+ ok = ssl:send(S, Msg).
+
+connect_dist_c(S) ->
+ Test = binary_to_list(term_to_binary({erlang,term})),
+ {ok, Test} = ssl:recv(S, 0, 10000),
+ ok.
+
+
+%%--------------------------------------------------------------------
+peername(doc) ->
+ ["Test API function peername/1"];
+
+peername(suite) ->
+ [];
+
+peername(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, peername_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, peername_result, []}},
+ {options, [{port, 0} | ClientOpts]}]),
+
+ ClientPort = ssl_test_lib:inet_port(Client),
+ ServerIp = ssl_test_lib:node_to_hostip(ServerNode),
+ ClientIp = ssl_test_lib:node_to_hostip(ClientNode),
+ ServerMsg = {ok, {ClientIp, ClientPort}},
+ ClientMsg = {ok, {ServerIp, Port}},
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+peername_result(S) ->
+ ssl:peername(S).
+
+%%--------------------------------------------------------------------
+sockname(doc) ->
+ ["Test API function sockname/1"];
+
+sockname(suite) ->
+ [];
+
+sockname(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, sockname_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, sockname_result, []}},
+ {options, [{port, 0} | ClientOpts]}]),
+
+ ClientPort = ssl_test_lib:inet_port(Client),
+ ServerIp = ssl_test_lib:node_to_hostip(ServerNode),
+ ClientIp = ssl_test_lib:node_to_hostip(ClientNode),
+ ServerMsg = {ok, {ServerIp, Port}},
+ ClientMsg = {ok, {ClientIp, ClientPort}},
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+sockname_result(S) ->
+ ssl:sockname(S).
+
+%%--------------------------------------------------------------------
+cipher_suites(doc) ->
+ ["Test API function cipher_suites/0"];
+
+cipher_suites(suite) ->
+ [];
+
+cipher_suites(Config) when is_list(Config) ->
+ MandatoryCipherSuite = {rsa,'3des_ede_cbc',sha,no_export},
+ [_|_] = Suites = ssl:cipher_suites(),
+ true = lists:member(MandatoryCipherSuite, Suites).
+%%--------------------------------------------------------------------
+socket_options(doc) ->
+ ["Test API function getopts/2 and setopts/2"];
+
+socket_options(suite) ->
+ [];
+
+socket_options(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Values = [{mode, list}, {packet, 0}, {header, 0},
+ {active, true}],
+ %% Shall be the reverse order of Values!
+ Options = [active, header, packet, mode],
+
+ NewValues = [{mode, binary}, {active, once}],
+ %% Shall be the reverse order of NewValues!
+ NewOptions = [active, mode],
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, socket_options_result,
+ [Options, Values, NewOptions, NewValues]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, socket_options_result,
+ [Options, Values, NewOptions, NewValues]}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) ->
+ %% Test get/set emulated opts
+ {ok, DefaultValues} = ssl:getopts(Socket, Options),
+ ssl:setopts(Socket, NewValues),
+ {ok, NewValues} = ssl:getopts(Socket, NewOptions),
+ %% Test get/set inet opts
+ {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]),
+ ok.
+
+%%--------------------------------------------------------------------
+misc_ssl_options(doc) ->
+ ["Test what happens when we give valid options"];
+
+misc_ssl_options(suite) ->
+ [];
+
+misc_ssl_options(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% Chek that ssl options not tested elsewhere are filtered away e.i. not passed to inet.
+ TestOpts = [{depth, 1},
+ {key, undefined},
+ {password, []},
+ {reuse_session, fun(_,_,_,_) -> true end},
+ {debug, []},
+ {cb_info, {gen_tcp, tcp, tcp_closed}}],
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, TestOpts ++ ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, TestOpts ++ ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+versions(doc) ->
+ ["Test API function versions/0"];
+
+versions(suite) ->
+ [];
+
+versions(Config) when is_list(Config) ->
+ [_|_] = Versions = ssl:versions(),
+ test_server:format("~p~n", [Versions]).
+
+%%--------------------------------------------------------------------
+send_recv(doc) ->
+ [""];
+
+send_recv(suite) ->
+ [];
+
+send_recv(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client =
+ ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+send_close(doc) ->
+ [""];
+
+send_close(suite) ->
+ [];
+
+send_close(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect,
+ [Hostname,Port,[binary, {active, false}, {reuseaddr, true}]]),
+ {ok, SslS} = rpc:call(ClientNode, ssl, connect,
+ [TcpS,[{active, false}|ClientOpts]]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), self(), Server]),
+ ok = ssl:send(SslS, "Hello world"),
+ {ok,<<"Hello world">>} = ssl:recv(SslS, 11),
+ gen_tcp:close(TcpS),
+ {error, _} = ssl:send(SslS, "Hello world"),
+ ssl_test_lib:close(Server).
+
+%%--------------------------------------------------------------------
+close_transport_accept(doc) ->
+ ["Tests closing ssl socket when waiting on ssl:transport_accept/1"];
+
+close_transport_accept(suite) ->
+ [];
+
+close_transport_accept(Config) when is_list(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = 0,
+ Opts = [{active, false} | ServerOpts],
+ {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]),
+ spawn_link(fun() ->
+ test_server:sleep(?SLEEP),
+ rpc:call(ServerNode, ssl, close, [ListenSocket])
+ end),
+ case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of
+ {error, closed} ->
+ ok;
+ Other ->
+ exit({?LINE, Other})
+ end.
+
+%%--------------------------------------------------------------------
+dh_params(doc) ->
+ ["Test to specify DH-params file in server."];
+
+dh_params(suite) ->
+ [];
+
+dh_params(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ DataDir = ?config(data_dir, Config),
+ DHParamFile = filename:join(DataDir, "dHParam.pem"),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{dhfile, DHParamFile} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options,
+ [{ciphers,[{dhe_rsa,aes_256_cbc,sha,ignore}]} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+upgrade(doc) ->
+ ["Test that you can upgrade an tcp connection to an ssl connection"];
+
+upgrade(suite) ->
+ [];
+
+upgrade(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+
+ Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ upgrade_result, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_upgrade_client([{node, ClientNode},
+ {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, upgrade_result, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+upgrade_result(Socket) ->
+ ok = ssl:send(Socket, "Hello world"),
+ %% Make sure binary is inherited from tcp socket and that we do
+ %% not get the list default!
+ receive
+ {ssl, _, <<"Hello world">>} ->
+ ok
+ end.
+
+%%--------------------------------------------------------------------
+upgrade_with_timeout(doc) ->
+ ["Test ssl_accept/3"];
+
+upgrade_with_timeout(suite) ->
+ [];
+
+upgrade_with_timeout(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+
+ Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {?MODULE,
+ upgrade_result, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_upgrade_client([{node, ClientNode},
+ {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, upgrade_result, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ClientOpts}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+tcp_connect(doc) ->
+ ["Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"];
+
+tcp_connect(suite) ->
+ [];
+
+tcp_connect(Config) when is_list(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ TcpOpts = [binary, {reuseaddr, true}],
+
+ Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {timeout, 5000},
+ {mfa, {?MODULE, dummy, []}},
+ {tcp_options, TcpOpts},
+ {ssl_options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]),
+ test_server:format("Testcase ~p connected to Server ~p ~n", [self(), Server]),
+ gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"),
+
+ receive
+ {tcp_closed, Socket} ->
+ receive
+ {Server, {error, Error}} ->
+ test_server:format("Error ~p", [Error])
+ end
+ end,
+ ssl_test_lib:close(Server).
+
+
+dummy(_Socket) ->
+ %% Should not happen as the ssl connection will not be established
+ %% due to fatal handshake failiure
+ exit(kill).
+
+%%--------------------------------------------------------------------
+ipv6(doc) ->
+ ["Test ipv6."];
+ipv6(suite) ->
+ [];
+ipv6(Config) when is_list(Config) ->
+ {ok, Hostname0} = inet:gethostname(),
+
+ case lists:member(list_to_atom(Hostname0), ?config(ipv6_hosts, Config)) of
+ true ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} =
+ ssl_test_lib:run_where(Config, ipv6),
+ Server = ssl_test_lib:start_server([{node, ServerNode},
+ {port, 0}, {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options,
+ [inet6, {active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options,
+ [inet6, {active, false} | ClientOpts]}]),
+
+ test_server:format("Testcase ~p, Client ~p Server ~p ~n",
+ [self(), Client, Server]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client);
+ false ->
+ {skip, "Host does not support IPv6"}
+ end.
+
+%%--------------------------------------------------------------------
+
+ekeyfile(doc) ->
+ ["Test what happens with an invalid key file"];
+
+ekeyfile(suite) ->
+ [];
+
+ekeyfile(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ BadOpts = ?config(server_bad_key, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, BadOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {from, self()}, {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, {error, ekeyfile}, Client,
+ {error, closed}).
+
+%%--------------------------------------------------------------------
+
+ecertfile(doc) ->
+ ["Test what happens with an invalid cert file"];
+
+ecertfile(suite) ->
+ [];
+
+ecertfile(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerBadOpts = ?config(server_bad_cert, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerBadOpts}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, {error, ecertfile}, Client,
+ {error, closed}).
+
+
+%%--------------------------------------------------------------------
+ecacertfile(doc) ->
+ ["Test what happens with an invalid cacert file"];
+
+ecacertfile(suite) ->
+ [];
+
+ecacertfile(Config) when is_list(Config) ->
+ ClientOpts = [{reuseaddr, true}|?config(client_opts, Config)],
+ ServerBadOpts = [{reuseaddr, true}|?config(server_bad_ca, Config)],
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server0 =
+ ssl_test_lib:start_server_error([{node, ServerNode},
+ {port, 0}, {from, self()},
+ {options, ServerBadOpts}]),
+
+ Port0 = ssl_test_lib:inet_port(Server0),
+
+
+ Client0 =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port0}, {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server0, {error, ecacertfile},
+ Client0, {error, closed}),
+
+ File0 = proplists:get_value(cacertfile, ServerBadOpts),
+ File = File0 ++ "do_not_exit.pem",
+ ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)],
+
+ Server1 =
+ ssl_test_lib:start_server_error([{node, ServerNode},
+ {port, 0}, {from, self()},
+ {options, ServerBadOpts1}]),
+
+ Port1 = ssl_test_lib:inet_port(Server1),
+
+ Client1 =
+ ssl_test_lib:start_client_error([{node, ClientNode},
+ {port, Port1}, {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server1, {error, ecacertfile},
+ Client1, {error, closed}),
+ ok.
+
+
+
+%%--------------------------------------------------------------------
+eoptions(doc) ->
+ ["Test what happens when we give invalid options"];
+
+eoptions(suite) ->
+ [];
+
+eoptions(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) ->
+ ssl_test_lib:check_result(Server,
+ {error, {eoptions, {sslv2, Option}}},
+ Client,
+ {error, {eoptions, {sslv2, Option}}});
+ (Client, Server, Option) ->
+ ssl_test_lib:check_result(Server,
+ {error, {eoptions, Option}},
+ Client,
+ {error, {eoptions, Option}})
+ end,
+
+ TestOpts = [{versions, [sslv2, sslv3]},
+ {ssl_imp, cool},
+ {verify, 4},
+ {verify_fun, function},
+ {fail_if_no_peer_cert, 0},
+ {verify_client_once, 1},
+ {validate_extensions_fun, function},
+ {depth, four},
+ {certfile, 'cert.pem'},
+ {keyfile,'key.pem' },
+ {password, foo},
+ {cacertfile, ""},
+ {dhfile,'dh.pem' },
+ {ciphers, [{foo, bar, sha, ignore}]},
+ {reuse_session, foo},
+ {reuse_sessions, 0},
+ {renegotiate_at, "10"},
+ {debug, 1},
+ {mode, depech},
+ {packet, 8.0},
+ {packet_size, "2"},
+ {header, a},
+ {active, trice},
+ {key, 'key.pem' }],
+
+ [begin
+ Server =
+ ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, [TestOpt | ServerOpts]}]),
+ %% Will never reach a point where port is used.
+ Client =
+ ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0},
+ {host, Hostname}, {from, self()},
+ {options, [TestOpt | ClientOpts]}]),
+ Check(Client, Server, TestOpt),
+ ok
+ end || TestOpt <- TestOpts],
+ ok.
+
+%%--------------------------------------------------------------------
+shutdown(doc) ->
+ [""];
+
+shutdown(suite) ->
+ [];
+
+shutdown(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, shutdown_result, [server]}},
+ {options, [{exit_on_close, false},
+ {active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, shutdown_result, [client]}},
+ {options,
+ [{exit_on_close, false},
+ {active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+shutdown_result(Socket, server) ->
+ ssl:send(Socket, "Hej"),
+ ssl:shutdown(Socket, write),
+ {ok, "Hej hopp"} = ssl:recv(Socket, 8),
+ ok;
+
+shutdown_result(Socket, client) ->
+ {ok, "Hej"} = ssl:recv(Socket, 3),
+ ssl:send(Socket, "Hej hopp"),
+ ssl:shutdown(Socket, write),
+ ok.
+
+%%--------------------------------------------------------------------
+shutdown_write(doc) ->
+ [""];
+
+shutdown_write(suite) ->
+ [];
+
+shutdown_write(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, shutdown_write_result, [server]}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, shutdown_write_result, [client]}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
+
+shutdown_write_result(Socket, server) ->
+ test_server:sleep(?SLEEP),
+ ssl:shutdown(Socket, write);
+shutdown_write_result(Socket, client) ->
+ ssl:recv(Socket, 0).
+
+%%--------------------------------------------------------------------
+shutdown_both(doc) ->
+ [""];
+
+shutdown_both(suite) ->
+ [];
+
+shutdown_both(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, shutdown_both_result, [server]}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, shutdown_both_result, [client]}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, {error, closed}).
+
+shutdown_both_result(Socket, server) ->
+ test_server:sleep(?SLEEP),
+ ssl:shutdown(Socket, read_write);
+shutdown_both_result(Socket, client) ->
+ ssl:recv(Socket, 0).
+
+%%--------------------------------------------------------------------
+shutdown_error(doc) ->
+ [""];
+
+shutdown_error(suite) ->
+ [];
+
+shutdown_error(Config) when is_list(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ Port = ssl_test_lib:inet_port(node()),
+ {ok, Listen} = ssl:listen(Port, ServerOpts),
+ {error, enotconn} = ssl:shutdown(Listen, read_write),
+ ok = ssl:close(Listen),
+ {error, closed} = ssl:shutdown(Listen, read_write).
+
+%%--------------------------------------------------------------------
+ciphers(doc) ->
+ [""];
+
+ciphers(suite) ->
+ [];
+
+ciphers(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl:cipher_suites(),
+ Result = lists:map(fun(Cipher) ->
+ cipher(Cipher, Version, Config) end,
+ Ciphers),
+ case lists:flatten(Result) of
+ [] ->
+ ok;
+ Error ->
+ test_server:format("Cipher suite errors: ~p~n", [Error]),
+ test_server:fail(cipher_suite_failed_see_test_case_log)
+ end.
+
+cipher(CipherSuite, Version, Config) ->
+ process_flag(trap_exit, true),
+ test_server:format("Testing CipherSuite ~p~n", [CipherSuite]),
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
+
+ ServerMsg = ClientMsg = {ok, {Version, CipherSuite}},
+
+ Result = ssl_test_lib:wait_for_result(Server, ServerMsg,
+ Client, ClientMsg),
+ ssl_test_lib:close(Server),
+ receive
+ {'EXIT', Server, normal} ->
+ ok
+ end,
+ ssl_test_lib:close(Client),
+ receive
+ {'EXIT', Client, normal} ->
+ ok
+ end,
+ process_flag(trap_exit, false),
+ case Result of
+ ok ->
+ [];
+ Error ->
+ [{CipherSuite, Error}]
+ end.
+
+%%--------------------------------------------------------------------
+reuse_session(doc) ->
+ ["Test reuse of sessions (short handshake)"];
+
+reuse_session(suite) ->
+ [];
+
+reuse_session(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ Server ! listen,
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ ok;
+ {Client1, Other} ->
+ test_server:format("Expected: ~p, Unexpected: ~p~n",
+ [SessionInfo, Other]),
+ test_server:fail(session_not_reused)
+ end,
+
+ Server ! listen,
+
+ Client2 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, [{reuse_sessions, false}
+ | ClientOpts]}]),
+ receive
+ {Client2, SessionInfo} ->
+ test_server:fail(
+ session_reused_when_session_reuse_disabled_by_client);
+ {Client2, _} ->
+ ok
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ ssl_test_lib:close(Client2),
+
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options, [{reuse_sessions, false} | ServerOpts]}]),
+
+ Port1 = ssl_test_lib:inet_port(Server1),
+ Client3 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port1}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ SessionInfo1 =
+ receive
+ {Server1, Info1} ->
+ Info1
+ end,
+
+ Server1 ! listen,
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+
+ Client4 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port1}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ receive
+ {Client4, SessionInfo1} ->
+ test_server:fail(
+ session_reused_when_session_reuse_disabled_by_server);
+ {Client4, _Other} ->
+ ok
+ end,
+
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client3),
+ ssl_test_lib:close(Client4),
+ process_flag(trap_exit, false).
+
+
+session_info_result(Socket) ->
+ ssl:session_info(Socket).
+
+%%--------------------------------------------------------------------
+reuse_session_expired(doc) ->
+ ["Test sessions is not reused when it has expired"];
+
+reuse_session_expired(suite) ->
+ [];
+
+reuse_session_expired(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ Server ! listen,
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ ok;
+ {Client1, Other} ->
+ test_server:format("Expected: ~p, Unexpected: ~p~n",
+ [SessionInfo, Other]),
+ test_server:fail(session_not_reused)
+ end,
+
+ Server ! listen,
+
+ %% Make sure session is unregistered due to expiration
+ test_server:sleep((?EXPIRE+1) * 1000),
+
+ Client2 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client2, SessionInfo} ->
+ test_server:fail(session_reused_when_session_expired);
+ {Client2, _} ->
+ ok
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ ssl_test_lib:close(Client2),
+ process_flag(trap_exit, false).
+%%--------------------------------------------------------------------
+server_does_not_want_to_reuse_session(doc) ->
+ ["Test reuse of sessions (short handshake)"];
+
+server_does_not_want_to_reuse_session(suite) ->
+ [];
+
+server_does_not_want_to_reuse_session(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options, [{reuse_session, fun(_,_,_,_) ->
+ false
+ end} |
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ Server ! listen,
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ test_server:fail(session_reused_when_server_does_not_want_to);
+ {Client1, _Other} ->
+ ok
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+ process_flag(trap_exit, false).
+
+%%--------------------------------------------------------------------
+
+server_verify_peer_passive(doc) ->
+ ["Test server option verify_peer"];
+
+server_verify_peer_passive(suite) ->
+ [];
+
+server_verify_peer_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false}, {verify, verify_peer}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+server_verify_peer_active(doc) ->
+ ["Test server option verify_peer"];
+
+server_verify_peer_active(suite) ->
+ [];
+
+server_verify_peer_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{active, true}, {verify, verify_peer}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+server_verify_peer_active_once(doc) ->
+ ["Test server option verify_peer"];
+
+server_verify_peer_active_once(suite) ->
+ [];
+
+server_verify_peer_active_once(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active_once, []}},
+ {options, [{active, once}, {verify, verify_peer}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active_once, []}},
+ {options, [{active, once} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+server_verify_none_passive(doc) ->
+ ["Test server option verify_none"];
+
+server_verify_none_passive(suite) ->
+ [];
+
+server_verify_none_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false}, {verify, verify_none}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+server_verify_none_active(doc) ->
+ ["Test server option verify_none"];
+
+server_verify_none_active(suite) ->
+ [];
+
+server_verify_none_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{active, true}, {verify, verify_none} |
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+server_verify_none_active_once(doc) ->
+ ["Test server option verify_none"];
+
+server_verify_none_active_once(suite) ->
+ [];
+
+server_verify_none_active_once(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active_once, []}},
+ {options, [{active, once}, {verify, verify_none}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active_once, []}},
+ {options, [{active, once} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+
+server_verify_client_once_passive(doc) ->
+ ["Test server option verify_client_once"];
+
+server_verify_client_once_passive(suite) ->
+ [];
+
+server_verify_client_once_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false}, {verify, verify_peer},
+ {verify_client_once, true}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client0, ok),
+ ssl_test_lib:close(Client0),
+ Server ! listen,
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, result_ok, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client1, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+
+server_verify_client_once_active(doc) ->
+ ["Test server option verify_client_once"];
+
+server_verify_client_once_active(suite) ->
+ [];
+
+server_verify_client_once_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{active, once}, {verify, verify_peer},
+ {verify_client_once, true}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client0, ok),
+ ssl_test_lib:close(Client0),
+ Server ! listen,
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, result_ok, []}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client1, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client1).
+
+
+%%--------------------------------------------------------------------
+
+server_verify_client_once_active_once(doc) ->
+ ["Test server option verify_client_once"];
+
+server_verify_client_once_active_once(suite) ->
+ [];
+
+server_verify_client_once_active_once(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active_once, []}},
+ {options, [{active, once}, {verify, verify_peer},
+ {verify_client_once, true}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active_once, []}},
+ {options, [{active, once} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client0, ok),
+ ssl_test_lib:close(Client0),
+ Server ! listen,
+
+ Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, result_ok, []}},
+ {options, [{active, once} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client1, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client1).
+
+%%--------------------------------------------------------------------
+
+server_verify_no_cacerts(doc) ->
+ ["Test server must have cacerts if it wants to verify client"];
+
+server_verify_no_cacerts(suite) ->
+ [];
+
+server_verify_no_cacerts(Config) when is_list(Config) ->
+ ServerOpts = ServerOpts = ?config(server_opts, Config),
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, [{verify, verify_peer}
+ | ServerOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error, {eoptions, {cacertfile, ""}}}).
+
+%%--------------------------------------------------------------------
+
+server_require_peer_cert_ok(doc) ->
+ ["Test server option fail_if_no_peer_cert when peer sends cert"];
+
+server_require_peer_cert_ok(suite) ->
+ [];
+
+server_require_peer_cert_ok(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ ClientOpts = ?config(client_verification_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+server_require_peer_cert_fail(doc) ->
+ ["Test server option fail_if_no_peer_cert when peer doesn't send cert"];
+
+server_require_peer_cert_fail(suite) ->
+ [];
+
+server_require_peer_cert_fail(Config) when is_list(Config) ->
+ ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}
+ | ?config(server_verification_opts, Config)],
+ BadClientOpts = ?config(client_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, no_result, []}},
+ {options, [{active, false} | ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, no_result, []}},
+ {options, [{active, false} | BadClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error, esslaccept},
+ Client, {error, esslconnect}),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+client_verify_none_passive(doc) ->
+ ["Test client option verify_none"];
+
+client_verify_none_passive(suite) ->
+ [];
+
+client_verify_none_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result, []}},
+ {options, [{active, false},
+ {verify, verify_none}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+client_verify_none_active(doc) ->
+ ["Test client option verify_none"];
+
+client_verify_none_active(suite) ->
+ [];
+
+client_verify_none_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options, [{active, true}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active, []}},
+ {options, [{active, true},
+ {verify, verify_none}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+client_verify_none_active_once(doc) ->
+ ["Test client option verify_none"];
+
+client_verify_none_active_once(suite) ->
+ [];
+
+client_verify_none_active_once(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active_once, []}},
+ {options, [{active, once} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ %% TODO: send message to test process to make sure
+ %% verifyfun has beeen run as it has the same behavior as
+ %% the default fun
+ VerifyFun = fun([{bad_cert, unknown_ca}]) ->
+ true;
+ (_) ->
+ false
+ end,
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ send_recv_result_active_once,
+ []}},
+ {options, [{active, once},
+ {verify, verify_none},
+ {verify_fun, VerifyFun}
+ | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+
+%%--------------------------------------------------------------------
+client_renegotiate(doc) ->
+ ["Test ssl:renegotiate/1 on client."];
+
+client_renegotiate(suite) ->
+ [];
+
+client_renegotiate(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate, [Data]}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok, Server, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+server_renegotiate(doc) ->
+ ["Test ssl:renegotiate/1 on server."];
+
+server_renegotiate(suite) ->
+ [];
+
+server_renegotiate(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ renegotiate, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ok.
+
+%%--------------------------------------------------------------------
+client_no_wrap_sequence_number(doc) ->
+ ["Test that erlang client will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."];
+
+client_no_wrap_sequence_number(suite) ->
+ [];
+
+client_no_wrap_sequence_number(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlData = "From erlang to erlang",
+ N = 10,
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[ErlData, N+2]]}},
+ {options, [{reuse_sessions, false},
+ {renegotiate_at, N} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+server_no_wrap_sequence_number(doc) ->
+ ["Test that erlang server will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."];
+
+server_no_wrap_sequence_number(suite) ->
+ [];
+
+server_no_wrap_sequence_number(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+ N = 10,
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[Data, N+2]]}},
+ {options, [{renegotiate_at, N} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {options, [{reuse_sessions, false} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok),
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ok.
+
+%%--------------------------------------------------------------------
+extended_key_usage(doc) ->
+ ["Test cert that has a critical extended_key_usage extension"];
+
+extended_key_usage(suite) ->
+ [];
+
+extended_key_usage(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+ NewCertFile = filename:join(PrivDir, "cert.pem"),
+
+ {ok, [{cert, DerCert, _}]} = public_key:pem_to_der(CertFile),
+
+ {ok, [KeyInfo]} = public_key:pem_to_der(KeyFile),
+
+ {ok, Key} = public_key:decode_private_key(KeyInfo),
+
+ {ok, OTPCert} = public_key:pkix_decode_cert(DerCert, otp),
+
+ ExtKeyUsageExt = {'Extension', ?'id-ce-extKeyUsage', true, [?'id-kp-serverAuth']},
+
+ OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate,
+
+ Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions,
+
+ NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{extensions = [ExtKeyUsageExt |Extensions]},
+
+ NewDerCert = public_key:sign(NewOTPTbsCert, Key),
+
+ public_key:der_to_pem(NewCertFile, [{cert, NewDerCert}]),
+
+ NewServerOpts = [{certfile, NewCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, NewServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, ClientOpts}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+validate_extensions_fun(doc) ->
+ ["Test that it is possible to specify a validate_extensions_fun"];
+
+validate_extensions_fun(suite) ->
+ [];
+
+validate_extensions_fun(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+
+ Fun = fun(Extensions, State, _, AccError) ->
+ {Extensions, State, AccError}
+ end,
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options, [{validate_extensions_fun, Fun},
+ {verify, verify_peer} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, send_recv_result_active, []}},
+ {options,[{validate_extensions_fun, Fun} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+%%% Internal functions
+%%--------------------------------------------------------------------
+send_recv_result(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ {ok,"Hello world"} = ssl:recv(Socket, 11),
+ ok.
+
+send_recv_result_active(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ receive
+ {ssl, Socket, "Hello world"} ->
+ ok
+ end.
+
+send_recv_result_active_once(Socket) ->
+ ssl:send(Socket, "Hello world"),
+ receive
+ {ssl, Socket, "Hello world"} ->
+ ok
+ end.
+
+result_ok(_Socket) ->
+ ok.
+
+renegotiate(Socket, Data) ->
+ test_server:format("Renegotiating ~n", []),
+ Result = ssl:renegotiate(Socket),
+ test_server:format("Result ~p~n", [Result]),
+ ssl:send(Socket, Data),
+ case Result of
+ ok ->
+ ok;
+ %% It is not an error in erlang ssl
+ %% if peer rejects renegotiation.
+ %% Connection will stay up
+ {error, renegotiation_rejected} ->
+ ok;
+ Other ->
+ Other
+ end.
+
+session_cache_process_list(doc) ->
+ ["Test reuse of sessions (short handshake)"];
+
+session_cache_process_list(suite) ->
+ [];
+session_cache_process_list(Config) when is_list(Config) ->
+ session_cache_process(list,Config).
+
+session_cache_process_mnesia(doc) ->
+ ["Test reuse of sessions (short handshake)"];
+
+session_cache_process_mnesia(suite) ->
+ [];
+session_cache_process_mnesia(Config) when is_list(Config) ->
+ session_cache_process(mnesia,Config).
+
+session_cache_process(Type,Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ setup_session_cb(Type),
+
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options,
+ [{session_cache_cb, ?MODULE}|
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client0 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {ssl_test_lib, no_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ SessionInfo =
+ receive
+ {Server, Info} ->
+ Info
+ end,
+
+ Server ! listen,
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+
+ Client1 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+ receive
+ {Client1, SessionInfo} ->
+ ok;
+ {Client1, Other} ->
+ test_server:format("Expected: ~p, Unexpected: ~p~n",
+ [SessionInfo, Other]),
+ test_server:fail(session_not_reused)
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client0),
+ ssl_test_lib:close(Client1),
+
+ Server1 =
+ ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, session_info_result, []}},
+ {options,
+ [{reuse_sessions, false} | ServerOpts]}]),
+ Port1 = ssl_test_lib:inet_port(Server1),
+
+ Client3 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port1}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ SessionInfo1 =
+ receive
+ {Server1, Info1} ->
+ Info1
+ end,
+
+ Server1 ! listen,
+
+ %% Make sure session is registered
+ test_server:sleep(?SLEEP),
+
+ Client4 =
+ ssl_test_lib:start_client([{node, ClientNode},
+ {port, Port1}, {host, Hostname},
+ {mfa, {?MODULE, session_info_result, []}},
+ {from, self()}, {options, ClientOpts}]),
+
+ receive
+ {Client4, SessionInfo1} ->
+ test_server:fail(
+ session_reused_when_session_reuse_disabled_by_server);
+ {Client4, _Other} ->
+ ok
+ end,
+
+ ssl_test_lib:close(Server1),
+ ssl_test_lib:close(Client3),
+ ssl_test_lib:close(Client4),
+ process_flag(trap_exit, false).
+
+setup_session_cb(Type) ->
+ ssl_test = ets:new(ssl_test,[named_table, set,public]),
+ ets:insert(ssl_test, {type,Type}).
+
+session_cb() ->
+ [{type,Type}] = ets:lookup(ssl_test, type),
+ Type.
+
+init() ->
+ io:format("~p~n",[?LINE]),
+ case session_cb() of
+ list ->
+ spawn(fun() -> session_loop([]) end);
+ mnesia ->
+ mnesia:start(),
+ {atomic,ok} = mnesia:create_table(sess_cache, [])
+ end.
+
+terminate(Cache) ->
+ io:format("~p~n",[?LINE]),
+ case session_cb() of
+ list ->
+ Cache ! terminate;
+ mnesia ->
+ {atomic,ok} = mnesia:delete_table(sess_cache, [])
+ end.
+
+lookup(Cache, Key) ->
+ io:format("~p~n",[?LINE]),
+ case session_cb() of
+ list ->
+ Cache ! {self(), lookup, Key},
+ receive {Cache, Res} -> Res end;
+ mnesia ->
+ case mnesia:transaction(fun() ->
+ mnesia:read(sess_cache,
+ Key, read)
+ end) of
+ {atomic, [Session]} -> Session;
+ _ -> undefined
+ end
+ end.
+
+update(Cache, Key, Value) ->
+ io:format("~p~n",[?LINE]),
+ case session_cb() of
+ list ->
+ Cache ! {update, Key, Value};
+ mnesia ->
+ {atomic, ok} =
+ mnesia:transaction(fun() ->
+ mnesia:write(sess_cache,
+ Key, Value)
+ end)
+ end.
+
+delete(Cache, Key) ->
+ io:format("~p~n",[?LINE]),
+ case session_cb() of
+ list ->
+ Cache ! {delete, Key};
+ mnesia ->
+ {atomic, ok} =
+ mnesia:transaction(fun() ->
+ mnesia:delete(sess_cache, Key)
+ end)
+ end.
+
+foldl(Fun, Acc, Cache) ->
+ io:format("~p~n",[?LINE]),
+ case session_cb() of
+ list ->
+ Cache ! {self(),foldl,Fun,Acc},
+ receive {Cache, Res} -> Res end;
+ mnesia ->
+ Foldl = fun() ->
+ mnesia:foldl(Fun, Acc, sess_cache)
+ end,
+ {atomic, Res} = mnesia:transaction(Foldl),
+ Res
+ end.
+
+select_session(Cache, PartialKey) ->
+ io:format("~p~n",[?LINE]),
+ case session_cb() of
+ list ->
+ Cache ! {self(),select_session, PartialKey},
+ receive {Cache, Res} -> Res end;
+ mnesia ->
+ Sel = fun() ->
+ mnesia:select(Cache,
+ [{{{PartialKey,'$1'}, '$2'},
+ [],['$$']}])
+ end,
+ {atomic, Res} = mnesia:transaction(Sel),
+ Res
+ end.
+
+session_loop(Sess) ->
+ receive
+ terminate ->
+ ok;
+ {Pid, lookup, Key} ->
+ case lists:keysearch(Key,1,Sess) of
+ {value, {Key,Value}} ->
+ Pid ! {self(), Value};
+ _ ->
+ Pid ! {self(), undefined}
+ end,
+ session_loop(Sess);
+ {update, Key, Value} ->
+ session_loop([{Key,Value}|Sess]);
+ {delete, Key} ->
+ session_loop(lists:keydelete(Key,1,Sess));
+ {Pid,foldl,Fun,Acc} ->
+ Res = lists:foldl(Fun, Acc,Sess),
+ Pid ! {self(), Res},
+ session_loop(Sess);
+ {Pid,select_session,PKey} ->
+ Sel = fun({{Head, _},Session}, Acc) when Head =:= PKey ->
+ [Session|Acc];
+ (_,Acc) ->
+ Acc
+ end,
+ Pid ! {self(), lists:foldl(Sel, [], Sess)},
+ session_loop(Sess)
+ end.
+
+erlang_ssl_receive(Socket, Data) ->
+ receive
+ {ssl, Socket, Data} ->
+ io:format("Received ~p~n",[Data]),
+ ok;
+ Other ->
+ test_server:fail({unexpected_message, Other})
+ after ?SLEEP * 3 ->
+ test_server:fail({did_not_get, Data})
+ end.
+
diff --git a/lib/ssl/test/ssl_basic_SUITE_data/RAND b/lib/ssl/test/ssl_basic_SUITE_data/RAND
new file mode 100644
index 0000000000..70997bd01f
--- /dev/null
+++ b/lib/ssl/test/ssl_basic_SUITE_data/RAND
Binary files differ
diff --git a/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem b/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem
new file mode 100644
index 0000000000..feb581da30
--- /dev/null
+++ b/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAMY5VmCZ22ZEy/KO8kjt94PH7ZtSG0Z0zitlMlvd4VsNkDzXsVeu+wkH
+FGDC3h3vgv6iwXGCbmrSOVk/FPZbzLhwZ8aLnkUFOBbOvVvb1JptQwOt8mf+eScG
+M2gGBktheQV5Nf1IrzOctG7VGt+neiqb/Y86uYCcDdL+M8++0qnLAgEC
+-----END DH PARAMETERS-----
diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl
new file mode 100644
index 0000000000..1bcb9a657b
--- /dev/null
+++ b/lib/ssl/test/ssl_packet_SUITE.erl
@@ -0,0 +1,1746 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_packet_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("test_server.hrl").
+
+-define(BYTE(X), X:8/unsigned-big-integer).
+-define(UINT16(X), X:16/unsigned-big-integer).
+-define(UINT24(X), X:24/unsigned-big-integer).
+-define(UINT32(X), X:32/unsigned-big-integer).
+-define(UINT64(X), X:64/unsigned-big-integer).
+-define(STRING(X), ?UINT32((size(X))), (X)/binary).
+
+-define(byte(X), << ?BYTE(X) >> ).
+-define(uint16(X), << ?UINT16(X) >> ).
+-define(uint24(X), << ?UINT24(X) >> ).
+-define(uint32(X), << ?UINT32(X) >> ).
+-define(uint64(X), << ?UINT64(X) >> ).
+-define(TIMEOUT, 120000).
+
+-define(MANY, 1000).
+-define(SOME, 50).
+
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initialization 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) ->
+ crypto:start(),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ test_server:format("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(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) ->
+ ssl:stop(),
+ crypto:stop().
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, 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: 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.
+%% Description: Initialization before each test case
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ssl_test_lib:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, 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(_TestCase, Config) ->
+ Dog = ?config(watchdog, Config),
+ case Dog of
+ undefined ->
+ ok;
+ _ ->
+ test_server:timetrap_cancel(Dog)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> TestCases
+%% Clause - atom() - suite | doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%% Description: Returns a list of all test cases in this test suite
+%%--------------------------------------------------------------------
+all(doc) ->
+ ["Test that erlang:decode_packet/3 seems to be handled correctly."
+ "We only use the most basic packet types in our tests as testing of"
+ "the packet types are for inet to verify"
+ ];
+
+all(suite) ->
+ [packet_raw_passive_many_small,
+ packet_0_passive_many_small, packet_1_passive_many_small,
+ packet_2_passive_many_small, packet_4_passive_many_small,
+ packet_raw_passive_some_big, packet_0_passive_some_big,
+ packet_1_passive_some_big,
+ packet_2_passive_some_big, packet_4_passive_some_big,
+ packet_raw_active_once_many_small,
+ packet_0_active_once_many_small, packet_1_active_once_many_small,
+ packet_2_active_once_many_small, packet_4_active_once_many_small,
+ packet_raw_active_once_some_big,
+ packet_0_active_once_some_big, packet_1_active_once_some_big,
+ packet_2_active_once_some_big, packet_4_active_once_some_big,
+ packet_raw_active_many_small, packet_0_active_many_small,
+ packet_1_active_many_small,
+ packet_2_active_many_small, packet_4_active_many_small,
+ packet_raw_active_some_big, packet_0_active_some_big,
+ packet_1_active_some_big, packet_2_active_some_big,
+ packet_4_active_some_big,
+ packet_send_to_large,
+ packet_wait_passive, packet_wait_active,
+ packet_baddata_passive, packet_baddata_active,
+ packet_size_passive, packet_size_active,
+ packet_erl_decode,
+ packet_http_decode,
+ packet_http_bin_decode_multi
+ ].
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+packet_raw_passive_many_small(doc) ->
+ ["Test packet option {packet, raw} in passive mode."];
+
+packet_raw_passive_many_small(suite) ->
+ [];
+
+packet_raw_passive_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, raw}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_raw, [Data, ?MANY]}},
+ {options,
+ [{active, false},
+ {packet, raw} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+
+packet_raw_passive_some_big(doc) ->
+ ["Test packet option {packet, raw} in passive mode."];
+
+packet_raw_passive_some_big(suite) ->
+ [];
+
+packet_raw_passive_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_raw, [Data, ?SOME]}},
+ {options,
+ [{active, false},
+ {packet, raw} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_0_passive_many_small(doc) ->
+ ["Test packet option {packet, 0} in passive mode."];
+
+packet_0_passive_many_small(suite) ->
+ [];
+
+packet_0_passive_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 0}, equivalent to packet raw.",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_raw, [Data, ?MANY]}},
+ {options, [{active, false},
+ {packet, 0} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_0_passive_some_big(doc) ->
+ ["Test packet option {packet, 0} in passive mode."];
+
+packet_0_passive_some_big(suite) ->
+ [];
+
+packet_0_passive_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_raw, [Data, ?SOME]}},
+ {options, [{active, false},
+ {packet, 0} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_1_passive_many_small(doc) ->
+ ["Test packet option {packet, 1} in passive mode."];
+
+packet_1_passive_many_small(suite) ->
+ [];
+
+packet_1_passive_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 1}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 1}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [Data, ?MANY]}},
+ {options, [{active, false},
+ {packet, 1} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_1_passive_some_big(doc) ->
+ ["Test packet option {packet, 1} in passive mode."];
+
+packet_1_passive_some_big(suite) ->
+ [];
+
+packet_1_passive_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(255, "1")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 1}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [Data, ?SOME]}},
+ {options, [{active, false},
+ {packet, 1} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_2_passive_many_small(doc) ->
+ ["Test packet option {packet, 2} in passive mode"];
+
+packet_2_passive_many_small(suite) ->
+ [];
+
+packet_2_passive_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 2}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 2}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [Data, ?MANY]}},
+ {options, [{active, false},
+ {packet, 2} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_2_passive_some_big(doc) ->
+ ["Test packet option {packet, 2} in passive mode"];
+
+packet_2_passive_some_big(suite) ->
+ [];
+
+packet_2_passive_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 2}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [Data, ?SOME]}},
+ {options, [{active, false},
+ {packet, 2} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_4_passive_many_small(doc) ->
+ ["Test packet option {packet, 4} in passive mode"];
+
+packet_4_passive_many_small(suite) ->
+ [];
+
+packet_4_passive_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 4}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 4}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [Data, ?MANY]}},
+ {options, [{active, false},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_4_passive_some_big(doc) ->
+ ["Test packet option {packet, 4} in passive mode"];
+
+packet_4_passive_some_big(suite) ->
+ [];
+
+packet_4_passive_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 4}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [Data, ?SOME]}},
+ {options, [{active, false},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_raw_active_once_many_small(doc) ->
+ ["Test packet option {packet, raw} in active once mode."];
+
+packet_raw_active_once_many_small(suite) ->
+ [];
+
+packet_raw_active_once_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, raw}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, active_once_raw, [Data, ?MANY]}},
+ {options, [{active, once},
+ {packet, raw} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_raw_active_once_some_big(doc) ->
+ ["Test packet option {packet, raw} in active once mode."];
+
+packet_raw_active_once_some_big(suite) ->
+ [];
+
+packet_raw_active_once_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, active_once_raw, [Data, ?SOME]}},
+ {options, [{active, once},
+ {packet, raw} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_0_active_once_many_small(doc) ->
+ ["Test packet option {packet, 0} in active once mode."];
+
+packet_0_active_once_many_small(suite) ->
+ [];
+
+packet_0_active_once_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 0}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, active_once_raw,
+ [Data, ?MANY]}},
+ {options, [{active, once},
+ {packet, 0} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_0_active_once_some_big(doc) ->
+ ["Test packet option {packet, 0} in active once mode."];
+
+packet_0_active_once_some_big(suite) ->
+ [];
+
+packet_0_active_once_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,
+ [Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, active_once_raw,
+ [Data, ?SOME]}},
+ {options, [{active, once},
+ {packet, 0} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_1_active_once_many_small(doc) ->
+ ["Test packet option {packet, 1} in active once mode."];
+
+packet_1_active_once_many_small(suite) ->
+ [];
+
+packet_1_active_once_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 1}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 1}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_once_packet,
+ [Data, ?MANY]}},
+ {options, [{active, once},
+ {packet, 1} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_1_active_once_some_big(doc) ->
+ ["Test packet option {packet, 1} in active once mode."];
+
+packet_1_active_once_some_big(suite) ->
+ [];
+
+packet_1_active_once_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(255, "1")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 1}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_once_packet,
+ [Data, ?SOME]}},
+ {options, [{active, once},
+ {packet, 1} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_2_active_once_many_small(doc) ->
+ ["Test packet option {packet, 2} in active once mode"];
+
+packet_2_active_once_many_small(suite) ->
+ [];
+
+packet_2_active_once_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 2}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 2}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_once_packet,
+ [Data, ?MANY]}},
+ {options, [{active, once},
+ {packet, 2} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_2_active_once_some_big(doc) ->
+ ["Test packet option {packet, 2} in active once mode"];
+
+packet_2_active_once_some_big(suite) ->
+ [];
+
+packet_2_active_once_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 2}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_once_packet,
+ [Data, ?SOME]}},
+ {options, [{active, once},
+ {packet, 2} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_4_active_once_many_small(doc) ->
+ ["Test packet option {packet, 4} in active once mode"];
+
+packet_4_active_once_many_small(suite) ->
+ [];
+
+packet_4_active_once_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 4}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 4}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_once_packet,
+ [Data, ?MANY]}},
+ {options, [{active, once},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_4_active_once_some_big(doc) ->
+ ["Test packet option {packet, 4} in active once mode"];
+
+packet_4_active_once_some_big(suite) ->
+ [];
+
+packet_4_active_once_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 4}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_once_packet,
+ [Data, ?SOME]}},
+ {options, [{active, once},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_raw_active_many_small(doc) ->
+ ["Test packet option {packet, raw} in active mode."];
+
+packet_raw_active_many_small(suite) ->
+ [];
+
+packet_raw_active_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, raw}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, active_raw,
+ [Data, ?MANY]}},
+ {options, [{active, true},
+ {packet, raw} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_raw_active_some_big(doc) ->
+ ["Test packet option {packet, raw} in active mode."];
+
+packet_raw_active_some_big(suite) ->
+ [];
+
+packet_raw_active_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, active_raw, [Data, ?SOME]}},
+ {options, [{active, true},
+ {packet, raw} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_0_active_many_small(doc) ->
+ ["Test packet option {packet, 0} in active mode."];
+
+packet_0_active_many_small(suite) ->
+ [];
+
+packet_0_active_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 0}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?MANY]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, active_raw,
+ [Data, ?MANY]}},
+ {options, [{active, true},
+ {packet, 0} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_0_active_some_big(doc) ->
+ ["Test packet option {packet, 0} in active mode."];
+
+packet_0_active_some_big(suite) ->
+ [];
+
+packet_0_active_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_raw ,[Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, active_raw,
+ [Data, ?SOME]}},
+ {options, [{active, true},
+ {packet, 0} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_1_active_many_small(doc) ->
+ ["Test packet option {packet, 1} in active mode."];
+
+packet_1_active_many_small(suite) ->
+ [];
+
+packet_1_active_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 1}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 1}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_packet, [Data, ?MANY]}},
+ {options, [{active, true},
+ {packet, 1} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_1_active_some_big(doc) ->
+ ["Test packet option {packet, 1} in active mode."];
+
+packet_1_active_some_big(suite) ->
+ [];
+
+packet_1_active_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(255, "1")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 1}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_packet, [Data, ?SOME]}},
+ {options, [{active, true},
+ {packet, 1} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_2_active_many_small(doc) ->
+ ["Test packet option {packet, 2} in active mode"];
+
+packet_2_active_many_small(suite) ->
+ [];
+
+packet_2_active_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 2}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 2}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_packet, [Data, ?MANY]}},
+ {options, [{active, true},
+ {packet, 2} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_2_active_some_big(doc) ->
+ ["Test packet option {packet, 2} in active mode"];
+
+packet_2_active_some_big(suite) ->
+ [];
+
+packet_2_active_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 2}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_packet, [Data, ?SOME]}},
+ {options, [{active, true},
+ {packet, 2} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_4_active_many_small(doc) ->
+ ["Test packet option {packet, 4} in active mode"];
+
+packet_4_active_many_small(suite) ->
+ [];
+
+packet_4_active_many_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "Packet option is {packet, 4}",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?MANY]}},
+ {options, [{packet, 4}|ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_packet, [Data, ?MANY]}},
+ {options, [{active, true},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_4_active_some_big(doc) ->
+ ["Test packet option {packet, 4} in active mode"];
+
+packet_4_active_some_big(suite) ->
+ [];
+
+packet_4_active_some_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, ?SOME]}},
+ {options, [{packet, 4} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE,
+ active_packet, [Data, ?SOME]}},
+ {options, [{active, true},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_send_to_large(doc) ->
+ ["Test setting the packet option {packet, 2} on the send side"];
+
+packet_send_to_large(suite) -> [];
+
+packet_send_to_large(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = lists:append(lists:duplicate(30, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send, [Data, 1]}},
+ {options, [{packet, 1}| ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, active_packet, [Data, 1]}},
+ {options, [{active, true} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, {error, {badarg, {packet_to_large, 300, 255}}}),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+
+
+
+%%--------------------------------------------------------------------
+packet_wait_active(doc) ->
+ ["Test waiting when complete packages have not arrived"];
+
+packet_wait_active(suite) ->
+ [];
+
+packet_wait_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = list_to_binary(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_incomplete ,[Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, active_packet,
+ [binary_to_list(Data), ?SOME]}},
+ {options, [{active, true},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+%%--------------------------------------------------------------------
+packet_wait_passive(doc) ->
+ ["Test waiting when complete packages have not arrived"];
+
+packet_wait_passive(suite) ->
+ [];
+
+packet_wait_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = list_to_binary(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_incomplete ,[Data, ?SOME]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [binary_to_list(Data), ?SOME]}},
+ {options, [{active, false},
+ {packet, 4} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+packet_baddata_active(doc) ->
+ ["Test that if a bad packet arrives error msg is sent and socket is closed"];
+packet_baddata_active(suite) ->
+ [];
+
+packet_baddata_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = list_to_binary(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_incomplete ,[Data, 1]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, active_packet, [Data, 1]}},
+ {options, [{active, true},
+ {packet, cdr} |
+ ClientOpts]}]),
+ receive
+ {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok;
+ Unexpected ->
+ test_server:fail({unexpected, Unexpected})
+ end,
+
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+packet_baddata_passive(doc) ->
+ ["Test that if a bad packet arrives error msg is sent and socket is closed"];
+
+packet_baddata_passive(suite) ->
+ [];
+
+packet_baddata_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = list_to_binary(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_incomplete ,[Data, 1]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet,
+ [Data, 1]}},
+ {options, [{active, false},
+ {packet, cdr} |
+ ClientOpts]}]),
+
+ receive
+ {Client, {other, {error, {invalid_packet, _}},{error,closed}, 1}} -> ok;
+ Unexpected ->
+ test_server:fail({unexpected, Unexpected})
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+packet_size_active(doc) ->
+ ["Test that if a packet of size larger than packet_size arrives error msg is sent and socket is closed"];
+packet_size_active(suite) ->
+ [];
+
+packet_size_active(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = list_to_binary(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_incomplete ,[Data, 1]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, active_packet, [Data, 1]}},
+ {options, [{active, true},
+ {packet, 4}, {packet_size, 10} |
+ ClientOpts]}]),
+ receive
+ {Client, {other, {ssl_error, _Socket, {invalid_packet, _}},{error,closed},1}} -> ok;
+ Unexpected ->
+ test_server:fail({unexpected, Unexpected})
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+%%--------------------------------------------------------------------
+packet_size_passive(doc) ->
+ ["Test that if a packet of size larger than packet_size arrives error msg is sent and socket is closed"];
+packet_size_passive(suite) ->
+ [];
+
+packet_size_passive(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = list_to_binary(lists:duplicate(100, "1234567890")),
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, send_incomplete ,[Data, 1]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, passive_recv_packet, [Data, 1]}},
+ {options, [{active, false},
+ {packet, 4}, {packet_size, 30} |
+ ClientOpts]}]),
+ receive
+ {Client, {other, {error, {invalid_packet, _}},{error,closed},1}} -> ok;
+ Unexpected ->
+ test_server:fail({unexpected, Unexpected})
+ end,
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+%%--------------------------------------------------------------------
+packet_erl_decode(doc) ->
+ ["Test that packets of sent to erlang:decode_packet works, i.e. currently"
+ "asn1 | cdr | sunrm | fcgi | tpkt | line | http | http_bin"
+ ];
+packet_erl_decode(suite) ->
+ [];
+
+packet_erl_decode(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% A valid cdr packet
+ Data = <<71,73,79,80,1,2,2,1,0,0,0,41,0,0,0,0,0,0,0,0,0,0,0,1,78,
+ 69,79,0,0,0,0,2,0,10,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,4,49>>,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_packet_decode ,[Data]}},
+ {options, [{active, true}, binary, {packet, cdr}|ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_packet_decode, [Data]}},
+ {options, [{active, true}, binary | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+server_packet_decode(Socket, CDR) ->
+ receive
+ {ssl, Socket, CDR} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ ok = ssl:send(Socket, CDR),
+ receive
+ {ssl, Socket, CDR} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end,
+ ok = ssl:send(Socket, CDR),
+ ok.
+
+client_packet_decode(Socket, CDR) ->
+ <<P1:10/binary, P2/binary>> = CDR,
+ ok = ssl:send(Socket, P1),
+ ok = ssl:send(Socket, P2),
+ receive
+ {ssl, Socket, CDR} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ ssl:setopts(Socket, [{packet, cdr}]),
+ ok = ssl:send(Socket, CDR),
+ receive
+ {ssl, Socket, CDR} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end,
+ ok.
+
+%%--------------------------------------------------------------------
+packet_http_decode(doc) ->
+ ["Test setting the packet option {packet, http}"];
+packet_http_decode(suite) ->
+ [];
+
+packet_http_decode(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Request = "GET / HTTP/1.1\r\n"
+ "host: www.example.com\r\n"
+ "user-agent: HttpTester\r\n"
+ "\r\n",
+ Response = "HTTP/1.1 200 OK\r\n"
+ "\r\n"
+ "Hello!",
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_http_decode, [Response]}},
+ {options, [{active, true}, binary, {packet, http} |
+ ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_http_decode, [Request]}},
+ {options, [{active, true}, binary, {packet, http} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+server_http_decode(Socket, HttpResponse) ->
+ assert_packet_opt(Socket, http),
+ receive
+ {ssl, Socket, {http_request, 'GET', _, {1,1}}} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ assert_packet_opt(Socket, http),
+ receive
+ {ssl, Socket, {http_header, _, 'Host', _, "www.example.com"}} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end,
+ assert_packet_opt(Socket, http),
+ receive
+ {ssl, Socket, {http_header, _, 'User-Agent', _, "HttpTester"}} -> ok;
+ Other3 -> exit({?LINE, Other3})
+ end,
+ assert_packet_opt(Socket, http),
+ receive
+ {ssl, Socket, http_eoh} -> ok;
+ Other4 -> exit({?LINE, Other4})
+ end,
+ assert_packet_opt(Socket, http),
+ ok = ssl:send(Socket, HttpResponse),
+ ok.
+
+client_http_decode(Socket, HttpRequest) ->
+ ok = ssl:send(Socket, HttpRequest),
+ receive
+ {ssl, Socket, {http_response, {1,1}, 200, "OK"}} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ receive
+ {ssl, Socket, http_eoh} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end,
+ ok = ssl:setopts(Socket, [{packet, 0}]),
+ receive
+ {ssl, Socket, <<"Hello!">>} -> ok;
+ Other3 -> exit({?LINE, Other3})
+ end,
+ ok.
+
+%%--------------------------------------------------------------------
+packet_http_bin_decode_multi(doc) ->
+ ["Test setting the packet option {packet, http_bin} with multiple requests"];
+packet_http_bin_decode_multi(suite) ->
+ [];
+
+packet_http_bin_decode_multi(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Request = <<"GET / HTTP/1.1\r\n"
+ "host: www.example.com\r\n"
+ "user-agent: HttpTester\r\n"
+ "\r\n">>,
+ Response = <<"HTTP/1.1 200 OK\r\n"
+ "\r\n"
+ "Hello!">>,
+ NumMsgs = 3,
+
+ Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, server_http_bin_decode, [Response, NumMsgs]}},
+ {options, [{active, true}, binary, {packet, http_bin} |
+ ServerOpts]}]),
+
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, client_http_bin_decode, [Request, NumMsgs]}},
+ {options, [{active, true}, binary, {packet, http_bin} |
+ ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+server_http_bin_decode(Socket, HttpResponse, Count) when Count > 0 ->
+ assert_packet_opt(Socket, http_bin),
+ receive
+ {ssl, Socket, {http_request, 'GET', _, {1,1}}} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ assert_packet_opt(Socket, http_bin),
+ receive
+ {ssl, Socket, {http_header, _, 'Host', _, <<"www.example.com">>}} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end,
+ assert_packet_opt(Socket, http_bin),
+ receive
+ {ssl, Socket, {http_header, _, 'User-Agent', _, <<"HttpTester">>}} -> ok;
+ Other3 -> exit({?LINE, Other3})
+ end,
+ assert_packet_opt(Socket, http_bin),
+ receive
+ {ssl, Socket, http_eoh} -> ok;
+ Other4 -> exit({?LINE, Other4})
+ end,
+ assert_packet_opt(Socket, http_bin),
+ ok = ssl:send(Socket, HttpResponse),
+ server_http_bin_decode(Socket, HttpResponse, Count - 1);
+server_http_bin_decode(_, _, _) ->
+ ok.
+
+client_http_bin_decode(Socket, HttpRequest, Count) when Count > 0 ->
+ ok = ssl:send(Socket, HttpRequest),
+ receive
+ {ssl, Socket, {http_response, {1,1}, 200, <<"OK">>}} -> ok;
+ Other1 -> exit({?LINE, Other1})
+ end,
+ receive
+ {ssl, Socket, http_eoh} -> ok;
+ Other2 -> exit({?LINE, Other2})
+ end,
+ ok = ssl:setopts(Socket, [{packet, 0}]),
+ receive
+ {ssl, Socket, <<"Hello!">>} -> ok;
+ Other3 -> exit({?LINE, Other3})
+ end,
+ ok = ssl:setopts(Socket, [{packet, http_bin}]),
+ client_http_bin_decode(Socket, HttpRequest, Count - 1);
+client_http_bin_decode(_, _, _) ->
+ ok.
+
+%%--------------------------------------------------------------------
+%% Internal functions
+
+send_raw(_,_, 0) ->
+ no_result_msg;
+send_raw(Socket, Data, N) ->
+ ssl:send(Socket, Data),
+ send_raw(Socket, Data, N-1).
+
+passive_raw(_, _, 0) ->
+ ok;
+passive_raw(Socket, Data, N) ->
+ Length = length(Data),
+ {ok, Data} = ssl:recv(Socket, Length),
+ passive_raw(Socket, Data, N-1).
+
+passive_recv_packet(_, _, 0) ->
+ ok;
+passive_recv_packet(Socket, Data, N) ->
+ case ssl:recv(Socket, 0) of
+ {ok, Data} ->
+ passive_recv_packet(Socket, Data, N-1);
+ Other ->
+ {other, Other, ssl:session_info(Socket), N}
+ end.
+
+send(_,_, 0) ->
+ no_result_msg;
+send(Socket, Data, N) ->
+ case ssl:send(Socket, [Data]) of
+ ok ->
+ send(Socket, Data, N-1);
+ Other ->
+ Other
+ end.
+
+send_incomplete(Socket, Data, N) ->
+ send_incomplete(Socket, Data, N, <<>>).
+send_incomplete(Socket, _Data, 0, Prev) ->
+ ssl:send(Socket, Prev),
+ no_result_msg;
+send_incomplete(Socket, Data, N, Prev) ->
+ Length = size(Data),
+ <<Part1:42/binary, Rest/binary>> = Data,
+ ssl:send(Socket, [Prev, ?uint32(Length), Part1]),
+ send_incomplete(Socket, Data, N-1, Rest).
+
+active_once_raw(Socket, Data, N) ->
+ active_once_raw(Socket, Data, N, []).
+
+active_once_raw(_, _, 0, _) ->
+ ok;
+active_once_raw(Socket, Data, N, Acc) ->
+ receive
+ {ssl, Socket, Data} ->
+ ssl:setopts(Socket, [{active, once}]),
+ active_once_raw(Socket, Data, N-1, []);
+ {ssl, Socket, Other} ->
+ case Acc ++ Other of
+ Data ->
+ ssl:setopts(Socket, [{active, once}]),
+ active_once_raw(Socket, Data, N-1, []);
+ NewAcc ->
+ ssl:setopts(Socket, [{active, once}]),
+ active_once_raw(Socket, Data, N, NewAcc)
+ end
+ end.
+
+active_once_packet(_,_, 0) ->
+ ok;
+active_once_packet(Socket, Data, N) ->
+ receive
+ {ssl, Socket, Data} ->
+ ok
+ end,
+ ssl:setopts(Socket, [{active, once}]),
+ active_once_packet(Socket, Data, N-1).
+
+active_raw(Socket, Data, N) ->
+ active_raw(Socket, Data, N, []).
+
+active_raw(_, _, 0, _) ->
+ ok;
+active_raw(Socket, Data, N, Acc) ->
+ receive
+ {ssl, Socket, Data} ->
+ active_raw(Socket, Data, N-1, []);
+ {ssl, Socket, Other} ->
+ case Acc ++ Other of
+ Data ->
+ active_raw(Socket, Data, N-1, []);
+ NewAcc ->
+ active_raw(Socket, Data, NewAcc)
+ end
+ end.
+
+active_packet(_, _, 0) ->
+ ok;
+active_packet(Socket, Data, N) ->
+ receive
+ {ssl, Socket, Data} ->
+ active_packet(Socket, Data, N -1);
+ Other ->
+ {other, Other, ssl:session_info(Socket),N}
+ end.
+
+assert_packet_opt(Socket, Type) ->
+ {ok, [{packet, Type}]} = ssl:getopts(Socket, [packet]).
diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl
new file mode 100644
index 0000000000..a0aa92bdf2
--- /dev/null
+++ b/lib/ssl/test/ssl_payload_SUITE.erl
@@ -0,0 +1,726 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ssl_payload_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("test_server.hrl").
+
+-define(TIMEOUT, 600000).
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initialization 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) ->
+ crypto:start(),
+ ssl:start(),
+ make_certs:all(?config(data_dir, Config), ?config(priv_dir, Config)),
+ ssl_test_lib:cert_options(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) ->
+ ssl:stop(),
+ crypto:stop().
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, 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: 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.
+%% Description: Initialization before each test case
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ssl_test_lib:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, 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(_TestCase, Config) ->
+ Dog = ?config(watchdog, Config),
+ case Dog of
+ undefined ->
+ ok;
+ _ ->
+ test_server:timetrap_cancel(Dog)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> TestCases
+%% Clause - atom() - suite | doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%% Description: Returns a list of all test cases in this test suite
+%%--------------------------------------------------------------------
+all(doc) ->
+ ["Test payload over ssl in all socket modes, active, active_once,"
+ "and passive mode."];
+
+all(suite) ->
+ [server_echos_passive_small, server_echos_active_once_small,
+ server_echos_active_small,
+ client_echos_passive_small, client_echos_active_once_small,
+ client_echos_active_small,
+ server_echos_passive_big, server_echos_active_once_big,
+ server_echos_active_big,
+ client_echos_passive_big, client_echos_active_once_big,
+ client_echos_active_big,
+ server_echos_passive_huge, server_echos_active_once_huge,
+ server_echos_active_huge,
+ client_echos_passive_huge, client_echos_active_once_huge,
+ client_echos_active_huge
+ ].
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+server_echos_passive_small(doc) ->
+ ["Client sends 1000 bytes in passive mode to server, that receives them, "
+ "sends them back, and closes."];
+
+server_echos_passive_small(suite) ->
+ [];
+
+server_echos_passive_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_passive(Str, 1000, ClientOpts, ServerOpts,
+ ClientNode, ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+
+server_echos_active_once_small(doc) ->
+ ["Client sends 1000 bytes in active once mode to server, that receives "
+ " them, sends them back, and closes."];
+
+server_echos_active_once_small(suite) ->
+ [];
+
+server_echos_active_once_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_active_once(Str, 1000, ClientOpts, ServerOpts,
+ ClientNode, ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+
+server_echos_active_small(doc) ->
+ ["Client sends 1000 bytes in active mode to server, that receives them, "
+ "sends them back, and closes."];
+
+server_echos_active_small(suite) ->
+ [];
+
+server_echos_active_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_active(Str, 1000, ClientOpts, ServerOpts,
+ ClientNode, ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_passive_small(doc) ->
+ ["Server sends 1000 bytes in passive mode to client, that receives them, "
+ "sends them back, and closes."];
+
+client_echos_passive_small(suite) ->
+ [];
+
+client_echos_passive_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ client_echos_passive(Str, 1000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_active_once_small(doc) ->
+ ["Server sends 1000 bytes in active once mode to client, that receives "
+ "them, sends them back, and closes."];
+
+client_echos_active_once_small(suite) ->
+ [];
+
+client_echos_active_once_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ client_echos_active_once(Str, 1000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_active_small(doc) ->
+ ["Server sends 1000 bytes in active mode to client, that receives them, "
+ "sends them back, and closes."];
+
+client_echos_active_small(suite) ->
+ [];
+
+client_echos_active_small(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ client_echos_active(Str, 1000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+
+%%--------------------------------------------------------------------
+server_echos_passive_big(doc) ->
+ ["Client sends 50000 bytes to server in passive mode, that receives them, "
+ "sends them back, and closes."];
+
+server_echos_passive_big(suite) ->
+ [];
+
+server_echos_passive_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_passive(Str, 50000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+
+server_echos_active_once_big(doc) ->
+ ["Client sends 50000 bytes to server in active once mode, that receives "
+ "them, sends them back, and closes."];
+
+server_echos_active_once_big(suite) ->
+ [];
+
+server_echos_active_once_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_active_once(Str, 50000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+
+server_echos_active_big(doc) ->
+ ["Client sends 50000 bytes to server in active once mode, that receives "
+ " them, sends them back, and closes."];
+
+server_echos_active_big(suite) ->
+ [];
+
+server_echos_active_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_active(Str, 50000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_passive_big(doc) ->
+ ["Server sends 50000 bytes to client in passive mode, that receives them, "
+ "sends them back, and closes."];
+
+client_echos_passive_big(suite) ->
+ [];
+
+client_echos_passive_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ client_echos_passive(Str, 50000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_active_once_big(doc) ->
+ ["Server sends 50000 bytes to client in active once mode, that receives"
+ " them, sends them back, and closes."];
+
+client_echos_active_once_big(suite) ->
+ [];
+
+client_echos_active_once_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ client_echos_active_once(Str, 50000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_active_big(doc) ->
+ ["Server sends 50000 bytes to client in active mode, that receives them, "
+ "sends them back, and closes."];
+
+client_echos_active_big(suite) ->
+ [];
+
+client_echos_active_big(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ client_echos_active(Str, 50000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+server_echos_passive_huge(doc) ->
+ ["Client sends 500000 bytes to server in passive mode, that receives "
+ " them, sends them back, and closes."];
+
+server_echos_passive_huge(suite) ->
+ [];
+
+server_echos_passive_huge(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_passive(Str, 500000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+server_echos_active_once_huge(doc) ->
+ ["Client sends 500000 bytes to server in active once mode, that receives "
+ "them, sends them back, and closes."];
+
+server_echos_active_once_huge(suite) ->
+ [];
+
+server_echos_active_once_huge(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_active_once(Str, 500000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+server_echos_active_huge(doc) ->
+ ["Client sends 500000 bytes to server in active mode, that receives them, "
+ "sends them back, and closes."];
+
+server_echos_active_huge(suite) ->
+ [];
+
+server_echos_active_huge(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+
+ server_echos_active(Str, 500000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_passive_huge(doc) ->
+ ["Server sends 500000 bytes to client in passive mode, that receives "
+ "them, sends them back, and closes."];
+
+client_echos_passive_huge(suite) ->
+ [];
+
+client_echos_passive_huge(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+ client_echos_passive(Str, 500000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_active_once_huge(doc) ->
+ ["Server sends 500000 bytes to client in active once mode, that receives "
+ "them, sends them back, and closes."];
+
+client_echos_active_once_huge(suite) ->
+ [];
+
+client_echos_active_once_huge(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+ client_echos_active_once(Str, 500000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+client_echos_active_huge(doc) ->
+ ["Server sends 500000 bytes to client in active mode, that receives them, "
+ "sends them back, and closes."];
+
+client_echos_active_huge(suite) ->
+ [];
+
+client_echos_active_huge(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Str = "1234567890",
+ client_echos_active(Str, 500000, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname).
+
+%%--------------------------------------------------------------------
+
+server_echos_passive(Data, Length, ClientOpts, ServerOpts,
+ ClientNode, ServerNode, Hostname) ->
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, echoer,
+ [Data, Length]}},
+ {options,
+ [{active, false},{mode, binary}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, sender,
+ [Data,
+ Length]}},
+ {options,
+ [{active, false}, {mode, binary} |
+ ClientOpts]}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+server_echos_active_once(Data, Length, ClientOpts, ServerOpts, ClientNode,
+ ServerNode, Hostname) ->
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, echoer_once,
+ [Data, Length]}},
+ {options, [{active, once},
+ {mode, binary}|
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, sender_once,
+ [Data, Length]}},
+ {options, [{active, once},
+ {mode, binary} |
+ ClientOpts]}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+
+server_echos_active(Data, Length, ClientOpts, ServerOpts,
+ ClientNode, ServerNode, Hostname) ->
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, echoer_active,
+ [Data, Length]}},
+ {options,
+ [{active, true},
+ {mode, binary} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, sender_active,
+ [Data,
+ Length]}},
+ {options,
+ [{active, true}, {mode, binary}
+ | ClientOpts]}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+client_echos_passive(Data, Length, ClientOpts, ServerOpts,
+ ClientNode, ServerNode, Hostname) ->
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, sender,
+ [Data, Length]}},
+ {options,
+ [{active, false}, {mode, binary} |
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, echoer,
+ [Data,
+ Length]}},
+ {options,
+ [{active, false}, {mode, binary}
+ | ClientOpts]}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+client_echos_active_once(Data, Length,
+ ClientOpts, ServerOpts, ClientNode, ServerNode,
+ Hostname) ->
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, sender_once,
+ [Data, Length]}},
+ {options, [{active, once},
+ {mode, binary} |
+ ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, echoer_once,
+ [Data,
+ Length]}},
+ {options,[{active, once},
+ {mode, binary}
+ | ClientOpts]}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+client_echos_active(Data, Length, ClientOpts, ServerOpts, ClientNode,
+ ServerNode,
+ Hostname) ->
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, sender_active,
+ [Data, Length]}},
+ {options, [{active, true},
+ {mode, binary}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa,
+ {?MODULE, echoer_active,
+ [Data,
+ Length]}},
+ {options, [{active, true},
+ {mode, binary}
+ | ClientOpts]}]),
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client).
+
+send(_, _, _, 0,_) ->
+ ok;
+send(Socket, Data, Size, Repeate,F) ->
+ NewData = lists:duplicate(Size div 10, Data),
+ ssl:send(Socket, NewData),
+ F(),
+ send(Socket, Data, Size, Repeate - 1,F).
+
+sender(Socket, Data, Size) ->
+ ok = send(Socket, Data, Size, 100, fun() -> do_recv(Socket, Data, Size, <<>>, false) end),
+ test_server:format("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]),
+ ok.
+
+sender_once(Socket, Data, Size) ->
+ send(Socket, Data, Size, 100,
+ fun() -> do_active_once(Socket, Data, Size, <<>>, false) end),
+ test_server:format("Sender active once: ~p~n",
+ [ssl:getopts(Socket, [active])]),
+ ok.
+
+sender_active(Socket, Data, Size) ->
+ F = fun() -> do_active(Socket, Data, Size, <<>>, false) end,
+ send(Socket, Data, Size, 100, F),
+ test_server:format("Sender active: ~p~n", [ssl:getopts(Socket, [active])]),
+ ok.
+
+echoer(Socket, Data, Size) ->
+ test_server:format("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]),
+ echo(fun() -> do_recv(Socket, Data, Size, <<>>, true) end, 100).
+
+echoer_once(Socket, Data, Size) ->
+ test_server:format("Echoer active once: ~p ~n",
+ [ssl:getopts(Socket, [active])]),
+ echo(fun() -> do_active_once(Socket, Data, Size, <<>>, true) end, 100).
+
+echoer_active(Socket, Data, Size) ->
+ test_server:format("Echoer active: ~p~n", [ssl:getopts(Socket, [active])]),
+ echo(fun() -> do_active(Socket, Data, Size, <<>>, true) end, 100).
+
+echo(_Fun, 0) -> ok;
+echo(Fun, N) ->
+ Fun(),
+ echo(Fun, N-1).
+
+
+do_recv(_Socket, _Data, 0, _Acc, true) ->
+ ok;
+do_recv(_Socket, Data, 0, Acc, false) ->
+ Data = lists:sublist(binary_to_list(Acc), 10);
+
+do_recv(Socket, Data, Size, Acc, Echo) ->
+ {ok, NewData} = ssl:recv(Socket, 0),
+ NewSize = size(NewData),
+ case Echo of
+ true ->
+ ssl:send(Socket, NewData),
+ NewSize = size(NewData),
+ do_recv(Socket, Data, Size - NewSize, [], Echo);
+ false ->
+ case size(Acc) < 10 of
+ true ->
+ do_recv(Socket, Data, Size - NewSize,
+ <<Acc/binary, NewData/binary>>, Echo);
+ false ->
+ do_recv(Socket, Data, Size - NewSize, Acc, Echo)
+ end
+ end.
+
+do_active_once(_Socket, _Data, 0, _Acc, true) ->
+ ok;
+do_active_once(_Socket, Data, 0, Acc, false) ->
+ Data = lists:sublist(binary_to_list(Acc), 10);
+
+do_active_once(Socket, Data, Size, Acc, Echo) ->
+ receive
+ {ssl, Socket, NewData} ->
+ NewSize = size(NewData),
+ case Echo of
+ true ->
+ ssl:send(Socket, NewData),
+ ssl:setopts(Socket, [{active, once}]),
+ do_active_once(Socket, Data, Size - NewSize, [], Echo);
+ false ->
+ case size(Acc) < 10 of
+ true ->
+ ssl:setopts(Socket, [{active, once}]),
+ do_active_once(Socket, Data, Size - NewSize,
+ <<Acc/binary, NewData/binary>>,
+ Echo);
+ false ->
+ ssl:setopts(Socket, [{active, once}]),
+ do_active_once(Socket, Data,
+ Size - NewSize, Acc, Echo)
+ end
+ end
+ end.
+
+do_active(_Socket, _Data, 0, _Acc, true) ->
+ ok;
+do_active(_Socket, Data, 0, Acc, false) ->
+ Data = lists:sublist(binary_to_list(Acc), 10);
+
+do_active(Socket, Data, Size, Acc, Echo) ->
+ receive
+ {ssl, Socket, NewData} ->
+ NewSize = size(NewData),
+ case Echo of
+ true ->
+ ssl:send(Socket, NewData),
+ do_active(Socket, Data, Size - NewSize, [], Echo);
+ false ->
+ case size(Acc) < 10 of
+ true ->
+ do_active(Socket, Data, Size - NewSize,
+ <<Acc/binary, NewData/binary>>,
+ Echo);
+ false ->
+ do_active(Socket, Data,
+ Size - NewSize, Acc, Echo)
+ end
+ end
+ end.
diff --git a/lib/ssl/test/ssl_test_MACHINE.erl b/lib/ssl/test/ssl_test_MACHINE.erl
new file mode 100644
index 0000000000..e75f7079ed
--- /dev/null
+++ b/lib/ssl/test/ssl_test_MACHINE.erl
@@ -0,0 +1,935 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_test_MACHINE).
+
+-export([many_conns/0, mk_ssl_cert_opts/1, test_one_listener/7,
+ test_server_only/6]).
+
+-export([process_init/3, do_start/1]).
+
+
+-include("test_server.hrl").
+-include("ssl_test_MACHINE.hrl").
+
+-define(WAIT_TIMEOUT, 10000).
+-define(CLOSE_WAIT, 1000).
+
+%%
+%% many_conns() -> ManyConnections
+%%
+%% Choose a suitable number of "many connections" depending on platform
+%% and current limit for file descriptors.
+%%
+many_conns() ->
+ case os:type() of
+ {unix,_} -> many_conns_1();
+ _ -> 10
+ end.
+
+many_conns_1() ->
+ N0 = os:cmd("ulimit -n"),
+ N1 = lists:reverse(N0),
+ N2 = lists:dropwhile(fun($\r) -> true;
+ ($\n) -> true;
+ (_) -> false
+ end, N1),
+ N = list_to_integer(lists:reverse(N2)),
+ lists:min([(N - 10) div 2, 501]).
+
+%%
+%% mk_ssl_cert_opts(Config) -> {ok, {COpts, SOpts}}
+%%
+%%
+mk_ssl_cert_opts(_Config) ->
+ Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]),
+ COpts = [{cacertfile, filename:join([Dir, "client", "cacerts.pem"])},
+ {certfile, filename:join([Dir, "client", "cert.pem"])},
+ {keyfile, filename:join([Dir, "client", "key.pem"])}],
+ SOpts = [{cacertfile, filename:join([Dir, "server", "cacerts.pem"])},
+ {certfile, filename:join([Dir, "server", "cert.pem"])},
+ {keyfile, filename:join([Dir, "server", "key.pem"])}],
+ {ok, {COpts, SOpts}}.
+
+%%
+%% Cmds:
+%% {protomod, gen_tcp | ssl} default = ssl
+%% {serialize_accept, true | false} default = false
+%% {timeout, Timeout}
+%% {sockopts, Opts}
+%% {sslopts, Opts}
+%% {protocols, Protocols} [sslv2|sslv3|tlsv1]
+%% {listen, Port}
+%% {lsock, LSock} listen socket for acceptor
+%% peercert
+%% accept
+%% {connect, {Host, Port}}
+%% {recv, N}
+%% {send, N}
+%% {echo, N} async echo back
+%% close close connection socket
+%% {close, Time} wait time and then close socket
+%% lclose close listen socket
+%% await_close wait for close
+%% wait_sync listener's wait for sync from parent
+%% connection_info
+%% {exit, Reason} exit
+%%
+%%
+%% We cannot have more than `backlog' acceptors at the same time.
+%%
+
+
+%%
+%% test_one_listener(NConns, LCmds, ACmds, CCmds, Timeout, Suite, Config)
+%%
+%% Creates one client and one server node, and runs one listener on
+%% the server node (according to LCmds), and creates NConns acceptors
+%% on the server node, and the same number of connectors on the client
+%% node. The acceptors and and connectors execute according to ACmds
+%% and CCmds, respectively.
+%%
+%% It is a good idea to have the backlog size in LCmds set to
+%% be at least as large as NConns.
+%%
+test_one_listener(NConns, LCmds0, ACmds0, CCmds0, Timeout, Suite, Config) ->
+ ProtoMod = get_protomod(Config),
+ SerializeAccept = get_serialize_accept(Config),
+ ?line {ok, {CNode, SNode}} = start_client_server_nodes(Suite),
+ case ProtoMod of
+ ssl ->
+ ?line ok = start_ssl([CNode, SNode], Config);
+ gen_tcp ->
+ ok
+ end,
+ LCmds = [{protomod, ProtoMod}| LCmds0],
+ ACmds = [{protomod, ProtoMod}, {serialize_accept, SerializeAccept}|
+ ACmds0],
+ CCmds = [{protomod, ProtoMod}| CCmds0],
+
+ ?line {ok, Listener} = start_process(SNode, self(), LCmds, listener),
+ ?line {ok, LSock} = wait_lsock(Listener, ?WAIT_TIMEOUT),
+ ?line {ok, Accs0} = start_processes(NConns, SNode, self(),
+ [{lsock, LSock}| ACmds], acceptor),
+ Accs = case ProtoMod of
+ gen_tcp ->
+ [Acc1| Accs1] = Accs0,
+ Acc1 ! {continue_accept, self()},
+ Accs1;
+ ssl ->
+ Accs0
+ end,
+ ?line {ok, Conns} = start_processes(NConns, CNode, self(),
+ CCmds, connector),
+ ?line case wait_ack(Accs, Accs0 ++ Conns, Timeout) of
+ ok ->
+ ?line sync([Listener]),
+ ?line wait_ack([], [Listener], ?WAIT_TIMEOUT);
+ {error, Reason} ->
+ ?line stop_node(SNode),
+ ?line stop_node(CNode),
+ exit(Reason)
+ end,
+ ?line stop_node(SNode),
+ ?line stop_node(CNode),
+ ok.
+
+%%
+%% test_server_only(NConns, LCmds, ACmds, Timeout, Suite, Config)
+%%
+%% Creates only one server node, and runs one listener on
+%% the server node (according to LCmds), and creates NConns acceptors
+%% on the server node. The acceptors execute according to ACmds.
+%% There are no connectors.
+%%
+test_server_only(NConns, LCmds0, ACmds0, Timeout, Suite, Config) ->
+ ProtoMod = get_protomod(Config),
+ ?line {ok, SNode} = start_server_node(Suite),
+ case ProtoMod of
+ ssl ->
+ ?line ok = start_ssl([SNode], Config);
+ gen_tcp ->
+ ok
+ end,
+ LCmds = [{protomod, ProtoMod}| LCmds0],
+ ACmds = [{protomod, ProtoMod}| ACmds0],
+ ?line {ok, Listener} = start_process(SNode, self(), LCmds, listener),
+ ?line {ok, LSock} = wait_lsock(Listener, ?WAIT_TIMEOUT),
+ ?line {ok, Accs0} = start_processes(NConns, SNode, self(),
+ [{lsock, LSock}| ACmds], acceptor),
+ Accs = case ProtoMod of
+ gen_tcp ->
+ [Acc1| Accs1] = Accs0,
+ Acc1 ! {continue_accept, self()},
+ Accs1;
+ ssl ->
+ Accs0
+ end,
+ ?line case wait_ack(Accs, Accs0, Timeout) of
+ ok ->
+ ?line sync([Listener]),
+ ?line wait_ack([], [Listener], ?WAIT_TIMEOUT);
+ {error, Reason} ->
+ ?line stop_node(SNode),
+ exit(Reason)
+ end,
+ ?line stop_node(SNode),
+ ok.
+
+%%
+%% start_client_server_nodes(Suite) -> {ok, {CNode, SNode}}
+%%
+start_client_server_nodes(Suite) ->
+ {ok, CNode} = start_client_node(Suite),
+ {ok, SNode} = start_server_node(Suite),
+ {ok, {CNode, SNode}}.
+
+start_client_node(Suite) ->
+ start_node(lists:concat([Suite, "_client"])).
+
+start_server_node(Suite) ->
+ start_node(lists:concat([Suite, "_server"])).
+
+%%
+%% start_ssl(Nodes, Config)
+%%
+start_ssl(Nodes, Config) ->
+ Env0 = lists:flatten([Env00 || {env, Env00} <- Config]),
+ Env1 = case os:getenv("SSL_DEBUG") of
+ false ->
+ [];
+ _ ->
+ Dir = ?config(priv_dir, Config),
+ [{debug, true}, {debugdir, Dir}]
+ end,
+ Env = Env0 ++ Env1,
+ lists:foreach(
+ fun(Node) -> rpc:call(Node, ?MODULE, do_start, [Env]) end, Nodes),
+ ok.
+
+do_start(Env) ->
+ application:load(ssl),
+ lists:foreach(
+ fun({Par, Val}) -> application:set_env(ssl, Par, Val) end, Env),
+ application:start(ssl),
+ application:start(crypto).
+
+%%
+%% start_node(Name) -> {ok, Node}
+%% start_node(Name, ExtraParams) -> {ok, Node}
+%%
+start_node(Name) ->
+ start_node(Name, []).
+start_node(Name, ExtraParams) ->
+ Params = "-pa " ++ filename:dirname(code:which(?MODULE)) ++ " " ++
+ ExtraParams,
+ test_server:start_node(Name, slave, [{args, Params}]).
+
+stop_node(Node) ->
+ test_server:stop_node(Node).
+
+%%
+%% start_processes(N, Node, Parent, Cmds, Type) -> {ok, Pids}
+%%
+start_processes(M, Node, Parent, Cmds, Type) ->
+ start_processes1(0, M, Node, Parent, Cmds, Type, []).
+start_processes1(M, M, _, _, _, _, Pids) ->
+ {ok, lists:reverse(Pids)};
+start_processes1(N, M, Node, Parent, Cmds, Type, Pids) ->
+ {ok, Pid} = start_process(Node, Parent, Cmds, {Type, N + 1}),
+ start_processes1(N + 1, M, Node, Parent, Cmds, Type, [Pid| Pids]).
+
+%%
+%% start_process(Node, Parent, Cmds, Type) -> {ok, Pid}
+%%
+start_process(Node, Parent, Cmds0, Type) ->
+ Cmds = case os:type() of
+ {win32, _} ->
+ lists:map(fun(close) -> {close, ?CLOSE_WAIT};
+ (Term) -> Term end, Cmds0);
+ _ ->
+ Cmds0
+ end,
+ Pid = spawn_link(Node, ?MODULE, process_init, [Parent, Cmds, Type]),
+ {ok, Pid}.
+
+process_init(Parent, Cmds, Type) ->
+ ?debug("#### ~w start~n", [{Type, self()}]),
+ pre_main_loop(Cmds, #st{parent = Parent, type = Type}).
+
+%%
+%% pre_main_loop
+%%
+pre_main_loop([], St) ->
+ ?debug("#### ~w end~n", [{St#st.type, self()}]),
+ main_loop([], St);
+pre_main_loop(Cmds, St) ->
+ ?debug("#### ~w -> ~w~n",
+ [{St#st.type, self(), St#st.sock, St#st.port,
+ St#st.peer, St#st.active}, hd(Cmds)]),
+ main_loop(Cmds, St).
+
+%%
+%% main_loop(Cmds, St)
+%%
+main_loop([{protomod, ProtoMod}| Cmds], St) ->
+ pre_main_loop(Cmds, St#st{protomod = ProtoMod});
+
+main_loop([{serialize_accept, Bool}| Cmds], St) ->
+ pre_main_loop(Cmds, St#st{serialize_accept = Bool});
+
+main_loop([{sockopts, Opts}| Cmds], St) ->
+ pre_main_loop(Cmds, St#st{sockopts = Opts});
+
+main_loop([{sslopts, Opts}| Cmds], St) ->
+ pre_main_loop(Cmds, St#st{sslopts = Opts});
+
+main_loop([{protocols, Protocols}| Cmds], St) ->
+ pre_main_loop(Cmds, St#st{protocols = Protocols});
+
+main_loop([{timeout, T}| Cmds], St) ->
+ pre_main_loop(Cmds, St#st{timeout = T});
+
+main_loop([{lsock, LSock}| Cmds], St) ->
+ pre_main_loop(Cmds, St#st{lsock = LSock});
+
+main_loop([{seed, Data}| Cmds], St) ->
+ case ssl:seed("tjosan") of
+ ok ->
+ pre_main_loop(Cmds, St);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in seed: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([{listen, Port}| Cmds], St) ->
+ case listen(St, Port) of
+ {ok, LSock} ->
+ ack_lsock(St#st.parent, LSock),
+ NSt = get_active(St#st{port = Port, sock = LSock, lsock = LSock}),
+ pre_main_loop(Cmds, St);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in listen: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([accept| Cmds], St) ->
+ case St#st.serialize_accept of
+ true ->
+ Parent = St#st.parent,
+ receive
+ {continue_accept, Parent} ->
+ ok
+ end;
+ false ->
+ ok
+ end,
+ case accept(St) of
+ {ok, Sock, Port, Peer} ->
+ case St#st.serialize_accept of
+ true ->
+ St#st.parent ! {one_accept_done, self()};
+ false ->
+ ok
+ end,
+ NSt = get_active(St#st{sock = Sock, port = Port, peer = Peer}),
+ pre_main_loop(Cmds, NSt);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in accept: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([accept_timeout| Cmds], St) ->
+ case accept(St) of
+ {error, timeout} ->
+ pre_main_loop(Cmds, St);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in accept_timeout: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+
+main_loop([{connect, {Host, Port}}| Cmds], St) ->
+ case connect(St, Host, Port) of
+ {ok, Sock, LPort, Peer} ->
+ NSt = get_active(St#st{sock = Sock, port = LPort, peer = Peer}),
+ pre_main_loop(Cmds, NSt);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in connect: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([connection_info| Cmds], St) ->
+ case connection_info(St) of
+ {ok, ProtoInfo} ->
+ io:fwrite("Got connection_info:~n~p~n", [ProtoInfo]),
+ pre_main_loop(Cmds, St);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in connection_info: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([peercert| Cmds], St) ->
+ case peercert(St) of
+ {ok, Cert} ->
+ io:fwrite("Got cert:~n~p~n", [Cert]),
+ pre_main_loop(Cmds, St);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in peercert: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([nopeercert| Cmds], St) ->
+ case peercert(St) of
+ {error, Reason} ->
+ io:fwrite("Got no cert as expected. reason:~n~p~n", [Reason]),
+ pre_main_loop(Cmds, St);
+ {ok, Cert} ->
+ ?error("#### ~w(~w) in peercert: error: got cert: ~p~n",
+ [St#st.type, self(), Cert]),
+ exit(peercert)
+ end;
+
+main_loop([{recv, N}| Cmds], St) ->
+ recv_loop([{recv, N}| Cmds], fun recv/1, St); % Returns to main_loop/2.
+
+main_loop([{send, N}| Cmds], St) ->
+ Msg = mk_msg(N),
+ case send(St, Msg) of
+ ok ->
+ pre_main_loop(Cmds, St);
+ {error, Reason} ->
+ ?error("#### ~w(~w) in send: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([{echo, N}| Cmds], St) ->
+ recv_loop([{echo, N}| Cmds], fun echo/1, St); % Returns to main_loop/2.
+
+main_loop([{close, WaitTime}| Cmds], St) ->
+ wait(WaitTime),
+ pre_main_loop([close| Cmds], St);
+
+main_loop([close| Cmds], St) ->
+ case close(St) of
+ ok ->
+ pre_main_loop(Cmds, St#st{sock = nil});
+ {error, Reason} ->
+ ?error("#### ~w(~w) in close: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([lclose| Cmds], St) ->
+ case lclose(St) of
+ ok ->
+ pre_main_loop(Cmds, St#st{lsock = nil});
+ {error, Reason} ->
+ ?error("#### ~w(~w) in lclose: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([await_close| Cmds], St) ->
+ case await_close(St) of
+ ok ->
+ pre_main_loop(Cmds, St#st{sock = nil});
+ {error, Reason} ->
+ ?error("#### ~w(~w) in await_close: error: ~w~n",
+ [St#st.type, self(), Reason]),
+ exit(Reason)
+ end;
+
+main_loop([wait_sync| Cmds], St) ->
+ wait_sync(St),
+ pre_main_loop(Cmds, St);
+
+main_loop({exit, Reason}, _St) ->
+ exit(Reason);
+
+main_loop([], _St) ->
+ ok.
+
+%%
+%% recv_loop(Cmds, F, St)
+%%
+%% F = recv/1 | echo/1
+%%
+recv_loop([{_Tag, 0}| Cmds], _, St) ->
+ pre_main_loop(Cmds, St);
+recv_loop([{_Tag, N}| _Cmds], _, St) when N < 0 ->
+ ?error("#### ~w(~w) in recv_loop: error: too much: ~w~n",
+ [St#st.type, self(), N]),
+ exit(toomuch); % XXX or {error, Reason}?
+recv_loop([{Tag, N}| Cmds], F, St) ->
+ case F(St) of
+ {ok, Len} ->
+ NSt = St#st{active = new_active(St#st.active)},
+ if
+ Len == N ->
+ pre_main_loop(Cmds, NSt);
+ true ->
+ ?debug("#### ~w -> ~w~n",
+ [{NSt#st.type, self(), NSt#st.sock, NSt#st.port,
+ NSt#st.peer, NSt#st.active}, {Tag, N - Len}]),
+ recv_loop([{Tag, N - Len}| Cmds], F, NSt)
+ end;
+ {error, Reason} ->
+ ?error("#### ~w(~w) in recv_loop: error: ~w, ~w bytes remain~n",
+ [St#st.type, self(), Reason, N]),
+ exit(Reason)
+ end.
+
+new_active(once) ->
+ false;
+new_active(A) ->
+ A.
+
+get_active(St) ->
+ A = case proplists:get_value(active, St#st.sockopts, undefined) of
+ undefined ->
+ Mod = case St#st.protomod of
+ ssl ->
+ ssl;
+ gen_tcp ->
+ inet
+ end,
+ {ok, [{active, Ax}]} = Mod:getopts(St#st.sock, [active]),
+ Ax;
+ Ay ->
+ Ay
+ end,
+ ?debug("#### ~w(~w) get_active: ~p\n", [St#st.type, self(), A]),
+ St#st{active = A}.
+
+
+%%
+%% SOCKET FUNCTIONS
+%%
+
+%%
+%% ssl
+%%
+
+%%
+%% listen(St, LPort) -> {ok, LSock} | {error, Reason}
+%%
+listen(St, LPort) ->
+ case St#st.protomod of
+ ssl ->
+ ssl:listen(LPort, St#st.sockopts ++ St#st.sslopts);
+ gen_tcp ->
+ gen_tcp:listen(LPort, St#st.sockopts)
+ end.
+
+%%
+%% accept(St) -> {ok, Sock} | {error, Reason}
+%%
+accept(St) ->
+ case St#st.protomod of
+ ssl ->
+ case ssl:transport_accept(St#st.lsock, St#st.timeout) of
+ {ok, Sock} ->
+ case ssl:ssl_accept(Sock, St#st.timeout) of
+ ok ->
+ {ok, Port} = ssl:sockname(Sock),
+ {ok, Peer} = ssl:peername(Sock),
+ {ok, Sock, Port, Peer};
+ Other ->
+ Other
+ end;
+ Other ->
+ Other
+ end;
+ gen_tcp ->
+ case gen_tcp:accept(St#st.lsock, St#st.timeout) of
+ {ok, Sock} ->
+ {ok, Port} = inet:port(Sock),
+ {ok, Peer} = inet:peername(Sock),
+ {ok, Sock, Port, Peer};
+ Other ->
+ Other
+ end
+ end.
+
+%%
+%% connect(St, Host, Port) -> {ok, Sock} | {error, Reason}
+%%
+connect(St, Host, Port) ->
+
+ case St#st.protomod of
+ ssl ->
+ case ssl:connect(Host, Port, St#st.sockopts ++ St#st.sslopts,
+ St#st.timeout) of
+ {ok, Sock} ->
+ {ok, LPort} = ssl:sockname(Sock),
+ {ok, Peer} = ssl:peername(Sock),
+ {ok, Sock, LPort, Peer};
+ Other ->
+ Other
+ end;
+ gen_tcp ->
+ case gen_tcp:connect(Host, Port, St#st.sockopts, St#st.timeout) of
+ {ok, Sock} ->
+ {ok, LPort} = inet:port(Sock),
+ {ok, Peer} = inet:peername(Sock),
+ {ok, Sock, LPort, Peer};
+ Other ->
+ Other
+ end
+ end.
+
+%%
+%% peercert(St) -> {ok, Cert} | {error, Reason}
+%%
+peercert(St) ->
+ case St#st.protomod of
+ ssl ->
+ ssl:peercert(St#st.sock, [ssl]);
+ gen_tcp ->
+ {ok, <<>>}
+ end.
+
+%%
+%% connection_info(St) -> {ok, ProtoInfo} | {error, Reason}
+%%
+connection_info(St) ->
+ case St#st.protomod of
+ ssl ->
+ case ssl:connection_info(St#st.sock) of
+ Res = {ok, {Proto, _}} ->
+ case St#st.protocols of
+ [] ->
+ Res;
+ Protocols ->
+ case lists:member(Proto, Protocols) of
+ true ->
+ Res;
+ false ->
+ {error, Proto}
+ end
+ end;
+ Error ->
+ Error
+ end;
+ gen_tcp ->
+ {ok, <<>>}
+ end.
+
+%%
+%% close(St) -> ok | {error, Reason}
+%%
+
+close(St) ->
+ Mod = St#st.protomod,
+ case St#st.sock of
+ nil ->
+ ok;
+ _ ->
+ Mod:close(St#st.sock)
+ end.
+
+%%
+%% lclose(St) -> ok | {error, Reason}
+%%
+lclose(St) ->
+ Mod = St#st.protomod,
+ case St#st.lsock of
+ nil ->
+ ok;
+ _ ->
+ Mod:close(St#st.lsock)
+ end.
+
+%%
+%% recv(St) = {ok, Len} | {error, Reason}
+%%
+recv(St) ->
+ case do_recv(St) of
+ {ok, Msg} ->
+ {ok, length(Msg)};
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+do_recv(St) when St#st.active == false ->
+ %% First check that we do *not* have any ssl/gen_tcp messages in the
+ %% message queue, then call the receive function.
+ Sock = St#st.sock,
+ case St#st.protomod of
+ ssl ->
+ receive
+ M = {ssl, Sock, _Msg} ->
+ {error, {unexpected_messagex, M}};
+ M = {ssl_closed, Sock} ->
+ {error, {unexpected_message, M}};
+ M = {ssl_error, Sock, _Reason} ->
+ {error, {unexpected_message, M}}
+ after 0 ->
+ ssl:recv(St#st.sock, 0, St#st.timeout)
+ end;
+ gen_tcp ->
+ receive
+ M = {tcp, Sock, _Msg} ->
+ {error, {unexpected_message, M}};
+ M = {tcp_closed, Sock} ->
+ {error, {unexpected_message, M}};
+ M = {tcp_error, Sock, _Reason} ->
+ {error, {unexpected_message, M}}
+ after 0 ->
+ gen_tcp:recv(St#st.sock, 0, St#st.timeout)
+ end
+ end;
+do_recv(St) ->
+ Sock = St#st.sock,
+ Timeout = St#st.timeout,
+ case St#st.protomod of
+ ssl ->
+ receive
+ {ssl, Sock, Msg} ->
+ {ok, Msg};
+ {ssl_closed, Sock} ->
+ {error, closed};
+ {ssl_error, Sock, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ {error, timeout}
+ end;
+ gen_tcp ->
+ receive
+ {tcp, Sock, Msg} ->
+ {ok, Msg};
+ {tcp_closed, Sock} ->
+ {error, closed};
+ {tcp_error, Sock, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ {error, timeout}
+ end
+ end.
+
+%%
+%% echo(St) = {ok, Len} | {error, Reason}
+%%
+echo(St) ->
+ Sock = St#st.sock,
+ case do_recv(St) of
+ {ok, Msg} ->
+ Mod = St#st.protomod,
+ case Mod:send(Sock, Msg) of
+ ok ->
+ {ok, length(Msg)};
+ {error, Reason} ->
+ {error, Reason}
+ end;
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+%%
+%% send(St, Msg) -> ok | {error, Reason}
+%%
+send(St, Msg) ->
+ Mod = St#st.protomod,
+ Mod:send(St#st.sock, Msg).
+
+%%
+%% await_close(St) -> ok | {error, Reason}
+%%
+await_close(St) when St#st.active == false ->
+ %% First check that we do *not* have any ssl/gen_tcp messages in the
+ %% message queue, then call the receive function.
+ Sock = St#st.sock,
+ Res = case St#st.protomod of
+ ssl ->
+ receive
+ M = {ssl, Sock, _Msg0} ->
+ {error, {unexpected_message, M}};
+ M = {ssl_closed, Sock} ->
+ {error, {unexpected_message, M}};
+ M = {ssl_error, Sock, _Reason} ->
+ {error, {unexpected_message, M}}
+ after 0 ->
+ ok
+ end;
+ gen_tcp ->
+ receive
+ M = {tcp, Sock, _Msg0} ->
+ {error, {unexpected_message, M}};
+ M = {tcp_closed, Sock} ->
+ {error, {unexpected_message, M}};
+ M = {tcp_error, Sock, _Reason} ->
+ {error, {unexpected_message, M}}
+ after 0 ->
+ ok
+ end
+ end,
+ case Res of
+ ok ->
+ Mod = St#st.protomod,
+ case Mod:recv(St#st.sock, 0, St#st.timeout) of
+ {ok, _Msg} ->
+ {error, toomuch};
+ {error, _} ->
+ ok
+ end;
+ _ ->
+ Res
+ end;
+await_close(St) ->
+ Sock = St#st.sock,
+ Timeout = St#st.timeout,
+ case St#st.protomod of
+ ssl ->
+ receive
+ {ssl, Sock, _Msg} ->
+ {error, toomuch};
+ {ssl_closed, Sock} ->
+ ok;
+ {ssl_error, Sock, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ {error, timeout}
+ end;
+ gen_tcp ->
+ receive
+ {tcp, Sock, _Msg} ->
+ {error, toomuch};
+ {tcp_closed, Sock} ->
+ ok;
+ {tcp_error, Sock, Reason} ->
+ {error, Reason}
+ after Timeout ->
+ {error, timeout}
+ end
+ end.
+
+
+%%
+%% HELP FUNCTIONS
+%%
+
+wait_ack(_, [], _) ->
+ ok;
+wait_ack(AccPids0, Pids, Timeout) ->
+ ?debug("#### CONTROLLER: waiting for ~w~n", [Pids]),
+ receive
+ {one_accept_done, Pid} ->
+ case lists:delete(Pid, AccPids0) of
+ [] ->
+ wait_ack([], Pids, Timeout);
+ [AccPid| AccPids1] ->
+ AccPid ! {continue_accept, self()},
+ wait_ack(AccPids1, Pids, Timeout)
+ end;
+ {'EXIT', Pid, normal} ->
+ wait_ack(AccPids0, lists:delete(Pid, Pids), Timeout);
+ {'EXIT', Pid, Reason} ->
+ ?error("#### CONTROLLER got abnormal exit: ~w, ~w~n",
+ [Pid, Reason]),
+ {error, Reason}
+ after Timeout ->
+ ?error("#### CONTROLLER exiting because of timeout = ~w~n",
+ [Timeout]),
+ {error, Timeout}
+ end.
+
+
+%%
+%% ack_lsock(Pid, LSock)
+%%
+ack_lsock(Pid, LSock) ->
+ Pid ! {lsock, self(), LSock}.
+
+wait_lsock(Pid, Timeout) ->
+ receive
+ {lsock, Pid, LSock} ->
+ {ok, LSock}
+ after Timeout ->
+ exit(timeout)
+ end.
+
+%%
+%% sync(Pids)
+%%
+sync(Pids) ->
+ lists:foreach(fun (Pid) -> Pid ! {self(), sync} end, Pids).
+
+%%
+%% wait_sync(St)
+%%
+wait_sync(St) ->
+ Pid = St#st.parent,
+ receive
+ {Pid, sync} ->
+ ok
+ end.
+
+%%
+%% wait(Time)
+%%
+wait(Time) ->
+ receive
+ after Time ->
+ ok
+ end.
+
+%%
+%% mk_msg(Size)
+%%
+mk_msg(Size) ->
+ mk_msg(0, Size, []).
+
+mk_msg(_, 0, Acc) ->
+ Acc;
+mk_msg(Pos, Size, Acc) ->
+ C = (((Pos + Size) rem 256) - 1) band 255,
+ mk_msg(Pos, Size - 1, [C| Acc]).
+
+%%
+%% get_protomod(Config)
+%%
+get_protomod(Config) ->
+ case lists:keysearch(protomod, 1, Config) of
+ {value, {_, ProtoMod}} ->
+ ProtoMod;
+ false ->
+ ssl
+ end.
+
+%%
+%% get_serialize_accept(Config)
+%%
+get_serialize_accept(Config) ->
+ case lists:keysearch(serialize_accept, 1, Config) of
+ {value, {_, Val}} ->
+ Val;
+ false ->
+ false
+ end.
+
diff --git a/lib/ssl/test/ssl_test_MACHINE.hrl b/lib/ssl/test/ssl_test_MACHINE.hrl
new file mode 100644
index 0000000000..e78b33f505
--- /dev/null
+++ b/lib/ssl/test/ssl_test_MACHINE.hrl
@@ -0,0 +1,39 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+-record(st, {protomod = ssl,
+ serialize_accept = false,
+ parent = nil,
+ type = nil,
+ active = nil,
+ port = 0,
+ peer = nil,
+ lsock = nil,
+ sock = nil,
+ timeout = infinity,
+ sockopts = [],
+ sslopts = [],
+ protocols = []}).
+
+%%-define(debug(X, Y), io:format(X, Y)).
+-define(debug(X, Y), ok).
+-define(error(X, Y), io:format(X, Y)).
+
+-define(DEFAULT_TIMEOUT, 240000).
+
diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl
new file mode 100644
index 0000000000..00c5350ad0
--- /dev/null
+++ b/lib/ssl/test/ssl_test_lib.erl
@@ -0,0 +1,496 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+-module(ssl_test_lib).
+
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-record(sslsocket, { fd = nil, pid = nil}).
+
+timetrap(Time) ->
+ Mul = try
+ test_server:timetrap_scale_factor()
+ catch _:_ -> 1 end,
+ test_server:timetrap(1000+Time*Mul).
+
+%% For now always run locally
+run_where(_) ->
+ ClientNode = node(),
+ ServerNode = node(),
+ {ok, Host} = rpc:call(ServerNode, inet, gethostname, []),
+ {ClientNode, ServerNode, Host}.
+
+run_where(_, ipv6) ->
+ ClientNode = node(),
+ ServerNode = node(),
+ {ok, Host} = rpc:call(ServerNode, inet, gethostname, []),
+ {ClientNode, ServerNode, Host}.
+
+node_to_hostip(Node) ->
+ [_ , Host] = string:tokens(atom_to_list(Node), "@"),
+ {ok, Address} = inet:getaddr(Host, inet),
+ Address.
+
+start_server(Args) ->
+ Result = spawn_link(?MODULE, run_server, [Args]),
+ receive
+ {listen, up} ->
+ Result
+ end.
+
+run_server(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Port = proplists:get_value(port, Opts),
+ Options = proplists:get_value(options, Opts),
+ Pid = proplists:get_value(from, Opts),
+ test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]),
+ {ok, ListenSocket} = rpc:call(Node, ssl, listen, [Port, Options]),
+ Pid ! {listen, up},
+ send_selected_port(Pid, Port, ListenSocket),
+ run_server(ListenSocket, Opts).
+
+run_server(ListenSocket, Opts) ->
+ AcceptSocket = connect(ListenSocket, Opts),
+ Node = proplists:get_value(node, Opts),
+ Pid = proplists:get_value(from, Opts),
+ {Module, Function, Args} = proplists:get_value(mfa, Opts),
+ test_server:format("Server: apply(~p,~p,~p)~n",
+ [Module, Function, [AcceptSocket | Args]]),
+ case rpc:call(Node, Module, Function, [AcceptSocket | Args]) of
+ no_result_msg ->
+ ok;
+ Msg ->
+ test_server:format("Msg: ~p ~n", [Msg]),
+ Pid ! {self(), Msg}
+ end,
+ receive
+ listen ->
+ run_server(ListenSocket, Opts);
+ close ->
+ ok = rpc:call(Node, ssl, close, [AcceptSocket])
+ end.
+
+%%% To enable to test with s_client -reconnect
+connect(ListenSocket, Opts) ->
+ Node = proplists:get_value(node, Opts),
+ ReconnectTimes = proplists:get_value(reconnect_times, Opts, 0),
+ AcceptSocket = connect(ListenSocket, Node, 1 + ReconnectTimes, dummy),
+ case ReconnectTimes of
+ 0 ->
+ AcceptSocket;
+ _ ->
+ remove_close_msg(ReconnectTimes),
+ AcceptSocket
+ end.
+
+connect(_, _, 0, AcceptSocket) ->
+ AcceptSocket;
+connect(ListenSocket, Node, N, _) ->
+ test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]),
+ {ok, AcceptSocket} = rpc:call(Node, ssl, transport_accept,
+ [ListenSocket]),
+ test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]),
+ ok = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]),
+ connect(ListenSocket, Node, N-1, AcceptSocket).
+
+remove_close_msg(0) ->
+ ok;
+remove_close_msg(ReconnectTimes) ->
+ receive
+ {ssl_closed, _} ->
+ remove_close_msg(ReconnectTimes -1)
+ end.
+
+
+start_client(Args) ->
+ Result = spawn_link(?MODULE, run_client, [Args]),
+ receive
+ connected ->
+ Result
+ end.
+
+run_client(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Host = proplists:get_value(host, Opts),
+ Port = proplists:get_value(port, Opts),
+ Pid = proplists:get_value(from, Opts),
+ Options = proplists:get_value(options, Opts),
+ test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ case rpc:call(Node, ssl, connect, [Host, Port, Options]) of
+ {ok, Socket} ->
+ Pid ! connected,
+ test_server:format("Client: connected~n", []),
+ %% In specail cases we want to know the client port, it will
+ %% be indicated by sending {port, 0} in options list!
+ send_selected_port(Pid, proplists:get_value(port, Options), Socket),
+ {Module, Function, Args} = proplists:get_value(mfa, Opts),
+ test_server:format("Client: apply(~p,~p,~p)~n",
+ [Module, Function, [Socket | Args]]),
+ case rpc:call(Node, Module, Function, [Socket | Args]) of
+ no_result_msg ->
+ ok;
+ Msg ->
+ Pid ! {self(), Msg}
+ end,
+ receive
+ close ->
+ ok = rpc:call(Node, ssl, close, [Socket])
+ end;
+ {error, Reason} ->
+ test_server:format("Client: connection failed: ~p ~n", [Reason]),
+ Pid ! {self(), {error, Reason}}
+ end.
+
+close(Pid) ->
+ Pid ! close.
+
+check_result(Server, ServerMsg, Client, ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ Unexpected ->
+ Reason = {{expected, {Client, ClientMsg}},
+ {got, Unexpected}},
+ test_server:fail(Reason)
+ end;
+ {Client, ClientMsg} ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ Unexpected ->
+ Reason = {{expected, {Server, ClientMsg}},
+ {got, Unexpected}},
+ test_server:fail(Reason)
+ end;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ check_result(Server, ServerMsg, Client, ClientMsg);
+
+ Unexpected ->
+ Reason = {{expected, {Client, ClientMsg}},
+ {expected, {Server, ServerMsg}}, {got, Unexpected}},
+ test_server:fail(Reason)
+ end.
+
+check_result(Pid, Msg) ->
+ receive
+ {Pid, Msg} ->
+ ok;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ check_result(Pid,Msg);
+ Unexpected ->
+ Reason = {{expected, {Pid, Msg}},
+ {got, Unexpected}},
+ test_server:fail(Reason)
+ end.
+
+check_result_ignore_renegotiation_reject(Pid, Msg) ->
+ receive
+ {Pid, fail_session_fatal_alert_during_renegotiation} ->
+ test_server:comment("Server rejected old renegotiation"),
+ ok;
+ {ssl_error, _, esslconnect} ->
+ test_server:comment("Server rejected old renegotiation"),
+ ok;
+ {Pid, Msg} ->
+ ok;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ check_result(Pid,Msg);
+ Unexpected ->
+ Reason = {{expected, {Pid, Msg}},
+ {got, Unexpected}},
+ test_server:fail(Reason)
+ end.
+
+
+wait_for_result(Server, ServerMsg, Client, ClientMsg) ->
+ receive
+ {Server, ServerMsg} ->
+ receive
+ {Client, ClientMsg} ->
+ ok;
+ Unexpected ->
+ Unexpected
+ end;
+ {Client, ClientMsg} ->
+ receive
+ {Server, ServerMsg} ->
+ ok;
+ Unexpected ->
+ Unexpected
+ end;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ wait_for_result(Server, ServerMsg, Client, ClientMsg);
+ Unexpected ->
+ Unexpected
+ end.
+
+
+wait_for_result(Pid, Msg) ->
+ receive
+ {Pid, Msg} ->
+ ok;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ wait_for_result(Pid,Msg);
+ Unexpected ->
+ Unexpected
+ end.
+
+cert_options(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"]),
+ ServerKeyCertFile = filename:join([?config(priv_dir, Config),
+ "server", "keycert.pem"]),
+ ClientKeyCertFile = filename:join([?config(priv_dir, Config),
+ "client", "keycert.pem"]),
+
+ BadCaCertFile = filename:join([?config(priv_dir, Config),
+ "badcacert.pem"]),
+ BadCertFile = filename:join([?config(priv_dir, Config),
+ "badcert.pem"]),
+ BadKeyFile = filename:join([?config(priv_dir, Config),
+ "badkey.pem"]),
+ [{client_opts, [{ssl_imp, new},{reuseaddr, true}]},
+ {client_verification_opts, [{cacertfile, ClientCaCertFile},
+ {certfile, ClientCertFile},
+ {keyfile, ClientKeyFile},
+ {ssl_imp, new}]},
+ {server_opts, [{ssl_imp, new},{reuseaddr, true},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
+ {server_verification_opts, [{ssl_imp, new},{reuseaddr, true},
+ {cacertfile, ServerCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]},
+ {client_kc_opts, [{certfile, ClientKeyCertFile}, {ssl_imp, new}]},
+ {server_kc_opts, [{ssl_imp, new},{reuseaddr, true},
+ {certfile, ServerKeyCertFile}]},
+ {client_bad_ca, [{cacertfile, BadCaCertFile},
+ {certfile, ClientCertFile},
+ {keyfile, ClientKeyFile},
+ {ssl_imp, new}]},
+ {client_bad_cert, [{cacertfile, ClientCaCertFile},
+ {certfile, BadCertFile},
+ {keyfile, ClientKeyFile},
+ {ssl_imp, new}]},
+ {server_bad_ca, [{ssl_imp, new},{cacertfile, BadCaCertFile},
+ {certfile, ServerCertFile},
+ {keyfile, ServerKeyFile}]},
+ {server_bad_cert, [{ssl_imp, new},{cacertfile, ServerCaCertFile},
+ {certfile, BadCertFile}, {keyfile, ServerKeyFile}]},
+ {server_bad_key, [{ssl_imp, new},{cacertfile, ServerCaCertFile},
+ {certfile, ServerCertFile}, {keyfile, BadKeyFile}]}
+ | Config].
+
+
+start_upgrade_server(Args) ->
+ Result = spawn_link(?MODULE, run_upgrade_server, [Args]),
+ receive
+ {listen, up} ->
+ Result
+ end.
+
+run_upgrade_server(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Port = proplists:get_value(port, Opts),
+ TimeOut = proplists:get_value(timeout, Opts, infinity),
+ TcpOptions = proplists:get_value(tcp_options, Opts),
+ SslOptions = proplists:get_value(ssl_options, Opts),
+ Pid = proplists:get_value(from, Opts),
+
+ test_server:format("gen_tcp:listen(~p, ~p)~n", [Port, TcpOptions]),
+ {ok, ListenSocket} = rpc:call(Node, gen_tcp, listen, [Port, TcpOptions]),
+ Pid ! {listen, up},
+ send_selected_port(Pid, Port, ListenSocket),
+ test_server:format("gen_tcp:accept(~p)~n", [ListenSocket]),
+ {ok, AcceptSocket} = rpc:call(Node, gen_tcp, accept, [ListenSocket]),
+
+ try
+ {ok, SslAcceptSocket} = case TimeOut of
+ infinity ->
+ test_server:format("ssl:ssl_accept(~p, ~p)~n",
+ [AcceptSocket, SslOptions]),
+ rpc:call(Node, ssl, ssl_accept,
+ [AcceptSocket, SslOptions]);
+ _ ->
+ test_server:format("ssl:ssl_accept(~p, ~p, ~p)~n",
+ [AcceptSocket, SslOptions, TimeOut]),
+ rpc:call(Node, ssl, ssl_accept,
+ [AcceptSocket, SslOptions, TimeOut])
+ end,
+ {Module, Function, Args} = proplists:get_value(mfa, Opts),
+ Msg = rpc:call(Node, Module, Function, [SslAcceptSocket | Args]),
+ Pid ! {self(), Msg},
+ receive
+ close ->
+ ok = rpc:call(Node, ssl, close, [SslAcceptSocket])
+ end
+ catch error:{badmatch, Error} ->
+ Pid ! {self(), Error}
+ end.
+
+start_upgrade_client(Args) ->
+ spawn_link(?MODULE, run_upgrade_client, [Args]).
+
+run_upgrade_client(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Host = proplists:get_value(host, Opts),
+ Port = proplists:get_value(port, Opts),
+ Pid = proplists:get_value(from, Opts),
+ TcpOptions = proplists:get_value(tcp_options, Opts),
+ SslOptions = proplists:get_value(ssl_options, Opts),
+
+ test_server:format("gen_tcp:connect(~p, ~p, ~p)~n",
+ [Host, Port, TcpOptions]),
+ {ok, Socket} = rpc:call(Node, gen_tcp, connect, [Host, Port, TcpOptions]),
+
+ send_selected_port(Pid, Port, Socket),
+
+ test_server:format("ssl:connect(~p, ~p)~n", [Socket, SslOptions]),
+ {ok, SslSocket} = rpc:call(Node, ssl, connect, [Socket, SslOptions]),
+
+ {Module, Function, Args} = proplists:get_value(mfa, Opts),
+ test_server:format("apply(~p, ~p, ~p)~n",
+ [Module, Function, [SslSocket | Args]]),
+ Msg = rpc:call(Node, Module, Function, [SslSocket | Args]),
+ Pid ! {self(), Msg},
+ receive
+ close ->
+ ok = rpc:call(Node, ssl, close, [SslSocket])
+ end.
+
+start_server_error(Args) ->
+ Result = spawn_link(?MODULE, run_server_error, [Args]),
+ receive
+ {listen, up} ->
+ Result
+ end.
+
+run_server_error(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Port = proplists:get_value(port, Opts),
+ Options = proplists:get_value(options, Opts),
+ Pid = proplists:get_value(from, Opts),
+ test_server:format("ssl:listen(~p, ~p)~n", [Port, Options]),
+ case rpc:call(Node, ssl, listen, [Port, Options]) of
+ {ok, ListenSocket} ->
+ %% To make sure error_client will
+ %% get {error, closed} and not {error, connection_refused}
+ Pid ! {listen, up},
+ send_selected_port(Pid, Port, ListenSocket),
+ test_server:format("ssl:transport_accept(~p)~n", [ListenSocket]),
+ case rpc:call(Node, ssl, transport_accept, [ListenSocket]) of
+ {error, _} = Error ->
+ Pid ! {self(), Error};
+ {ok, AcceptSocket} ->
+ test_server:format("ssl:ssl_accept(~p)~n", [AcceptSocket]),
+ Error = rpc:call(Node, ssl, ssl_accept, [AcceptSocket]),
+ Pid ! {self(), Error}
+ end;
+ Error ->
+ %% Not really true but as this is an error test
+ %% this is what we want.
+ Pid ! {listen, up},
+ Pid ! {self(), Error}
+ end.
+
+start_client_error(Args) ->
+ spawn_link(?MODULE, run_client_error, [Args]).
+
+run_client_error(Opts) ->
+ Node = proplists:get_value(node, Opts),
+ Host = proplists:get_value(host, Opts),
+ Port = proplists:get_value(port, Opts),
+ Pid = proplists:get_value(from, Opts),
+ Options = proplists:get_value(options, Opts),
+ test_server:format("ssl:connect(~p, ~p, ~p)~n", [Host, Port, Options]),
+ Error = rpc:call(Node, ssl, connect, [Host, Port, Options]),
+ Pid ! {self(), Error}.
+
+inet_port(Pid) when is_pid(Pid)->
+ receive
+ {Pid, {port, Port}} ->
+ Port
+ end;
+
+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}.
+
+no_result(_) ->
+ no_result_msg.
+
+trigger_renegotiate(Socket, [ErlData, N]) ->
+ [{session_id, Id} | _ ] = ssl:session_info(Socket),
+ trigger_renegotiate(Socket, ErlData, N, Id).
+
+trigger_renegotiate(Socket, _, 0, Id) ->
+ test_server:sleep(1000),
+ case ssl:session_info(Socket) of
+ [{session_id, Id} | _ ] ->
+ fail_session_not_renegotiated;
+ %% Tests that uses this function will not reuse
+ %% sessions so if we get a new session id the
+ %% renegotiation has succeeded.
+ [{session_id, _} | _ ] ->
+ ok;
+ {error, closed} ->
+ fail_session_fatal_alert_during_renegotiation;
+ {error, timeout} ->
+ fail_timeout
+ end;
+
+trigger_renegotiate(Socket, ErlData, N, Id) ->
+ ssl:send(Socket, ErlData),
+ trigger_renegotiate(Socket, ErlData, N-1, Id).
+
+
+send_selected_port(Pid, 0, #sslsocket{} = Socket) ->
+ {ok, {_, NewPort}} = ssl:sockname(Socket),
+ Pid ! {self(), {port, NewPort}};
+send_selected_port(Pid, 0, Socket) ->
+ {ok, {_, NewPort}} = inet:sockname(Socket),
+ Pid ! {self(), {port, NewPort}};
+send_selected_port(_,_,_) ->
+ ok.
diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl
new file mode 100644
index 0000000000..cbf0447bf0
--- /dev/null
+++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl
@@ -0,0 +1,1070 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% 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.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+
+-module(ssl_to_openssl_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("test_server.hrl").
+-include("test_server_line.hrl").
+-include("ssl_pkix.hrl").
+
+-define(TIMEOUT, 120000).
+-define(SLEEP, 1000).
+-define(OPENSSL_RENEGOTIATE, "r\n").
+-define(OPENSSL_QUIT, "Q\n").
+-define(OPENSSL_GARBAGE, "P\n").
+
+%% Test server callback functions
+%%--------------------------------------------------------------------
+%% Function: init_per_suite(Config) -> Config
+%% Config - [tuple()]
+%% A list of key/value pairs, holding the test case configuration.
+%% Description: Initialization 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) ->
+ case os:find_executable("openssl") of
+ false ->
+ {skip, "Openssl not found"};
+ _ ->
+ crypto:start(),
+ ssl:start(),
+ Result =
+ (catch make_certs:all(?config(data_dir, Config),
+ ?config(priv_dir, Config))),
+ test_server:format("Make certs ~p~n", [Result]),
+ ssl_test_lib:cert_options(Config)
+ end.
+
+%%--------------------------------------------------------------------
+%% 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) ->
+ ssl:stop(),
+ crypto:stop().
+
+%%--------------------------------------------------------------------
+%% Function: init_per_testcase(TestCase, 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: 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.
+%% Description: Initialization before each test case
+%%--------------------------------------------------------------------
+init_per_testcase(_TestCase, Config0) ->
+ Config = lists:keydelete(watchdog, 1, Config0),
+ Dog = ssl_test_lib:timetrap(?TIMEOUT),
+ [{watchdog, Dog} | Config].
+
+%%--------------------------------------------------------------------
+%% Function: end_per_testcase(TestCase, 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(_TestCase, Config) ->
+ Dog = ?config(watchdog, Config),
+ case Dog of
+ undefined ->
+ ok;
+ _ ->
+ test_server:timetrap_cancel(Dog)
+ end.
+
+%%--------------------------------------------------------------------
+%% Function: all(Clause) -> TestCases
+%% Clause - atom() - suite | doc
+%% TestCases - [Case]
+%% Case - atom()
+%% Name of a test case.
+%% Description: Returns a list of all test cases in this test suite
+%%--------------------------------------------------------------------
+all(doc) ->
+ ["Test erlangs ssl against openssl"];
+
+all(suite) ->
+ [erlang_client_openssl_server,
+ erlang_server_openssl_client,
+ erlang_server_openssl_client_reuse_session,
+ erlang_client_openssl_server_renegotiate,
+ erlang_client_openssl_server_no_wrap_sequence_number,
+ erlang_server_openssl_client_no_wrap_sequence_number,
+ erlang_client_openssl_server_no_server_ca_cert,
+ ssl3_erlang_client_openssl_server,
+ ssl3_erlang_server_openssl_client,
+ ssl3_erlang_client_openssl_server_client_cert,
+ ssl3_erlang_server_openssl_client_client_cert,
+ ssl3_erlang_server_erlang_client_client_cert,
+ tls1_erlang_client_openssl_server,
+ tls1_erlang_server_openssl_client,
+ tls1_erlang_client_openssl_server_client_cert,
+ tls1_erlang_server_openssl_client_client_cert,
+ tls1_erlang_server_erlang_client_client_cert,
+ ciphers,
+ erlang_client_bad_openssl_server
+ ].
+
+%% Test cases starts here.
+%%--------------------------------------------------------------------
+
+erlang_client_openssl_server(doc) ->
+ ["Test erlang client with openssl server"];
+erlang_client_openssl_server(suite) ->
+ [];
+erlang_client_openssl_server(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile,
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+
+%%--------------------------------------------------------------------
+erlang_server_openssl_client(doc) ->
+ ["Test erlang server with openssl client"];
+erlang_server_openssl_client(suite) ->
+ [];
+erlang_server_openssl_client(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_server_openssl_client_reuse_session(doc) ->
+ ["Test erlang server with openssl client that reconnects with the"
+ "same session id, to test reusing of sessions."];
+erlang_server_openssl_client_reuse_session(suite) ->
+ [];
+erlang_server_openssl_client_reuse_session(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE, erlang_ssl_receive, [Data]}},
+ {reconnect_times, 5},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost -reconnect",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_client_openssl_server_renegotiate(doc) ->
+ ["Test erlang client when openssl server issuses a renegotiate"];
+erlang_client_openssl_server_renegotiate(suite) ->
+ [];
+erlang_client_openssl_server_renegotiate(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlData = "From erlang to openssl",
+ OpenSslData = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ delayed_send, [[ErlData, OpenSslData]]}},
+ {options, ClientOpts}]),
+
+ port_command(OpensslPort, ?OPENSSL_RENEGOTIATE),
+ test_server:sleep(?SLEEP),
+ port_command(OpensslPort, OpenSslData),
+
+ %%ssl_test_lib:check_result(Client, ok),
+ %% Currently allow test case to not fail
+ %% if server requires secure renegotiation from RFC-5746
+ %% This should be removed as soon as we have implemented it.
+ ssl_test_lib:check_result_ignore_renegotiation_reject(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+erlang_client_openssl_server_no_wrap_sequence_number(doc) ->
+ ["Test that erlang client will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."];
+erlang_client_openssl_server_no_wrap_sequence_number(suite) ->
+ [];
+erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ ErlData = "From erlang to openssl\n",
+ N = 10,
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[ErlData, N+2]]}},
+ {options, [{reuse_sessions, false},
+ {renegotiate_at, N} | ClientOpts]}]),
+
+ %%ssl_test_lib:check_result(Client, ok),
+ %% Currently allow test case to not fail
+ %% if server requires secure renegotiation from RFC-5746
+ %% This should be removed as soon as we have implemented it.
+ ssl_test_lib:check_result_ignore_renegotiation_reject(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+erlang_server_openssl_client_no_wrap_sequence_number(doc) ->
+ ["Test that erlang client will renegotiate session when",
+ "max sequence number celing is about to be reached. Although"
+ "in the testcase we use the test option renegotiate_at"
+ " to lower treashold substantially."];
+
+erlang_server_openssl_client_no_wrap_sequence_number(suite) ->
+ [];
+erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ N = 10,
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib,
+ trigger_renegotiate, [[Data, N+2]]}},
+ {options, [{renegotiate_at, N} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ ssl_test_lib:close(Server),
+
+ close_port(OpenSslPort),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+
+erlang_client_openssl_server_no_server_ca_cert(doc) ->
+ ["Test erlang client when openssl server sends a cert chain not"
+ "including the ca cert. Explicitly test this even if it is"
+ "implicitly tested eleswhere."];
+erlang_client_openssl_server_no_server_ca_cert(suite) ->
+ [];
+erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down! Server needs to be closed first !!
+ close_port(OpensslPort),
+
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+ssl3_erlang_client_openssl_server(doc) ->
+ ["Test erlang client with openssl server"];
+ssl3_erlang_client_openssl_server(suite) ->
+ [];
+ssl3_erlang_client_openssl_server(Config) when is_list(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -ssl3",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ connection_info, [sslv3]}},
+ {options,
+ [{versions, [sslv3]} | ClientOpts]}]),
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Client),
+ %% Clean close down!
+ close_port(OpensslPort),
+ test_server:sleep(?SLEEP),
+ ok.
+
+%%--------------------------------------------------------------------
+
+ssl3_erlang_server_openssl_client(doc) ->
+ ["Test erlang server with openssl client"];
+ssl3_erlang_server_openssl_client(suite) ->
+ [];
+ssl3_erlang_server_openssl_client(Config) when is_list(Config) ->
+ ServerOpts = ?config(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, connection_info, [sslv3]}},
+ {options,
+ [{versions, [sslv3]} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost -ssl3",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ close_port(OpenSslPort), %% openssl server first
+ ssl_test_lib:close(Server),
+ test_server:sleep(?SLEEP),
+ ok.
+
+%%--------------------------------------------------------------------
+ssl3_erlang_client_openssl_server_client_cert(doc) ->
+ ["Test erlang client with openssl server when client sends cert"];
+ssl3_erlang_client_openssl_server_client_cert(suite) ->
+ [];
+ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down!
+ close_port(OpensslPort),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+ssl3_erlang_server_openssl_client_client_cert(doc) ->
+ ["Test erlang server with openssl client when client sends cert"];
+ssl3_erlang_server_openssl_client_client_cert(suite) ->
+ [];
+ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options,
+ [{verify , verify_peer}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ CaCertFile = proplists:get_value(cacertfile, ClientOpts),
+ CertFile = proplists:get_value(certfile, ClientOpts),
+ KeyFile = proplists:get_value(keyfile, ClientOpts),
+
+ Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
+ " -host localhost -ssl3",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ close_port(OpenSslPort), %% openssl server first
+ ssl_test_lib:close(Server),
+ %% Clean close down!
+ process_flag(trap_exit, false),
+ ok.
+
+
+%%--------------------------------------------------------------------
+
+ssl3_erlang_server_erlang_client_client_cert(doc) ->
+ ["Test erlang server with erlang client when client sends cert"];
+ssl3_erlang_server_erlang_client_client_cert(suite) ->
+ [];
+ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options,
+ [{verify , verify_peer}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl, send, [Data]}},
+ {options,
+ [{versions, [sslv3]} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+tls1_erlang_client_openssl_server(doc) ->
+ ["Test erlang client with openssl server"];
+tls1_erlang_client_openssl_server(suite) ->
+ [];
+tls1_erlang_client_openssl_server(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+ ClientOpts = ?config(client_opts, Config),
+
+
+ test_server:format("Server Opts", [ServerOpts]),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -tls1",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ connection_info, [tlsv1]}},
+ {options,
+ [{versions, [tlsv1]} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ ssl_test_lib:close(Client),
+ %% Clean close down!
+ close_port(OpensslPort),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+tls1_erlang_server_openssl_client(doc) ->
+ ["Test erlang server with openssl client"];
+tls1_erlang_server_openssl_client(suite) ->
+ [];
+tls1_erlang_server_openssl_client(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa,
+ {?MODULE, connection_info, [tlsv1]}},
+ {options,
+ [{versions, [tlsv1]} | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++
+ " -host localhost -tls1",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ %% Clean close down!
+ close_port(OpenSslPort),
+ ssl_test_lib:close(Server),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+tls1_erlang_client_openssl_server_client_cert(doc) ->
+ ["Test erlang client with openssl server when client sends cert"];
+tls1_erlang_client_openssl_server_client_cert(suite) ->
+ [];
+tls1_erlang_client_openssl_server_client_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Port = ssl_test_lib:inet_port(node()),
+ CaCertFile = proplists:get_value(cacertfile, ServerOpts),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options, ClientOpts}]),
+ port_command(OpensslPort, Data),
+
+ ssl_test_lib:check_result(Client, ok),
+
+ %% Clean close down!
+ close_port(OpensslPort),
+ ssl_test_lib:close(Client),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+
+tls1_erlang_server_openssl_client_client_cert(doc) ->
+ ["Test erlang server with openssl client when client sends cert"];
+tls1_erlang_server_openssl_client_client_cert(suite) ->
+ [];
+tls1_erlang_server_openssl_client_client_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+
+ {_, ServerNode, _} = ssl_test_lib:run_where(Config),
+
+ Data = "From openssl to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options,
+ [{verify , verify_peer}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ CaCertFile = proplists:get_value(cacertfile, ClientOpts),
+ CertFile = proplists:get_value(certfile, ClientOpts),
+ KeyFile = proplists:get_value(keyfile, ClientOpts),
+
+ Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile
+ ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++
+ " -host localhost -tls1",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+ port_command(OpenSslPort, Data),
+
+ ssl_test_lib:check_result(Server, ok),
+
+ %% Clean close down!
+ close_port(OpenSslPort),
+ ssl_test_lib:close(Server),
+ process_flag(trap_exit, false),
+ ok.
+
+%%--------------------------------------------------------------------
+tls1_erlang_server_erlang_client_client_cert(doc) ->
+ ["Test erlang server with erlang client when client sends cert"];
+tls1_erlang_server_erlang_client_client_cert(suite) ->
+ [];
+tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Data = "From erlang to erlang",
+
+ Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {?MODULE,
+ erlang_ssl_receive, [Data]}},
+ {options,
+ [{verify , verify_peer}
+ | ServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl, send, [Data]}},
+ {options,
+ [{versions, [tlsv1]} | ClientOpts]}]),
+
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ %% Clean close down!
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+
+ciphers(doc) ->
+ [""];
+
+ciphers(suite) ->
+ [];
+
+ciphers(Config) when is_list(Config) ->
+ Version =
+ ssl_record:protocol_version(ssl_record:highest_protocol_version([])),
+
+ Ciphers = ssl:cipher_suites(),
+ Result = lists:map(fun(Cipher) ->
+ cipher(Cipher, Version, Config) end,
+ Ciphers),
+ case lists:flatten(Result) of
+ [] ->
+ ok;
+ Error ->
+ test_server:format("Cipher suite errors: ~p~n", [Error]),
+ test_server:fail(cipher_suite_failed_see_test_case_log)
+ end.
+
+cipher(CipherSuite, Version, Config) ->
+ process_flag(trap_exit, true),
+ test_server:format("Testing CipherSuite ~p~n", [CipherSuite]),
+ ClientOpts = ?config(client_opts, Config),
+ ServerOpts = ?config(server_opts, Config),
+ {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, connection_info_result, []}},
+ {options,
+ [{ciphers,[CipherSuite]} |
+ ClientOpts]}]),
+
+ ClientMsg = {ok, {Version, CipherSuite}},
+
+ Result = ssl_test_lib:wait_for_result(Client, ClientMsg),
+
+ close_port(OpenSslPort),
+ %% Clean close down!
+ ssl_test_lib:close(Client),
+ receive
+ {'EXIT', Client, normal} ->
+ ok
+ end,
+
+ Return = case Result of
+ ok ->
+ [];
+ Error ->
+ [{CipherSuite, Error}]
+ end,
+ process_flag(trap_exit, false),
+ Return.
+
+%%--------------------------------------------------------------------
+erlang_client_bad_openssl_server(doc) ->
+ [""];
+erlang_client_bad_openssl_server(suite) ->
+ [];
+erlang_client_bad_openssl_server(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ ServerOpts = ?config(server_verification_opts, Config),
+ ClientOpts = ?config(client_verification_opts, Config),
+
+ {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config),
+
+ Port = ssl_test_lib:inet_port(node()),
+ CertFile = proplists:get_value(certfile, ServerOpts),
+ KeyFile = proplists:get_value(keyfile, ServerOpts),
+
+ Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++
+ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "",
+
+ test_server:format("openssl cmd: ~p~n", [Cmd]),
+
+ OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]),
+
+ wait_for_openssl_server(),
+
+ Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {?MODULE, server_sent_garbage, []}},
+ {options,
+ [{versions, [tlsv1]} | ClientOpts]}]),
+
+ %% Send garbage
+ port_command(OpensslPort, ?OPENSSL_GARBAGE),
+
+ test_server:sleep(?SLEEP),
+
+ Client ! server_sent_garbage,
+
+ ssl_test_lib:check_result(Client, true),
+
+ ssl_test_lib:close(Client),
+ %% Clean close down!
+ close_port(OpensslPort),
+ process_flag(trap_exit, false),
+ ok.
+%%--------------------------------------------------------------------
+
+erlang_ssl_receive(Socket, Data) ->
+ test_server:format("Connection info: ~p~n",
+ [ssl:connection_info(Socket)]),
+ receive
+ {ssl, Socket, Data} ->
+ io:format("Received ~p~n",[Data]),
+ %% open_ssl server sometimes hangs waiting in blocking read
+ ssl:send(Socket, "Got it"),
+ ok;
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ erlang_ssl_receive(Socket,Data);
+ Other ->
+ test_server:fail({unexpected_message, Other})
+ after 4000 ->
+ test_server:fail({did_not_get, Data})
+ end.
+
+connection_info(Socket, Version) ->
+ case ssl:connection_info(Socket) of
+ {ok, {Version, _} = Info} ->
+ test_server:format("Connection info: ~p~n", [Info]),
+ ok;
+ {ok, {OtherVersion, _}} ->
+ {wrong_version, OtherVersion}
+ end.
+
+connection_info_result(Socket) ->
+ ssl:connection_info(Socket).
+
+
+delayed_send(Socket, [ErlData, OpenSslData]) ->
+ test_server:sleep(?SLEEP),
+ ssl:send(Socket, ErlData),
+ erlang_ssl_receive(Socket, OpenSslData).
+
+close_port(Port) ->
+ port_command(Port, ?OPENSSL_QUIT),
+ %%catch port_command(Port, "quit\n"),
+ close_loop(Port, 500, false).
+
+close_loop(Port, Time, SentClose) ->
+ receive
+ {Port, {data,Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ close_loop(Port, Time, SentClose);
+ {ssl,_,Msg} ->
+ io:format("ssl Msg ~s~n",[Msg]),
+ close_loop(Port, Time, SentClose);
+ {Port, closed} ->
+ io:format("Port Closed~n",[]),
+ ok;
+ {'EXIT', Port, Reason} ->
+ io:format("Port Closed ~p~n",[Reason]),
+ ok;
+ Msg ->
+ io:format("Port Msg ~p~n",[Msg]),
+ close_loop(Port, Time, SentClose)
+ after Time ->
+ case SentClose of
+ false ->
+ io:format("Closing port ~n",[]),
+ catch erlang:port_close(Port),
+ close_loop(Port, Time, true);
+ true ->
+ io:format("Timeout~n",[])
+ end
+ end.
+
+
+server_sent_garbage(Socket) ->
+ receive
+ server_sent_garbage ->
+ {error, closed} == ssl:send(Socket, "data")
+ end.
+
+wait_for_openssl_server() ->
+ receive
+ {Port, {data, Debug}} when is_port(Port) ->
+ io:format("openssl ~s~n",[Debug]),
+ %% openssl has started make sure
+ %% it will be in accept. Parsing
+ %% output is too error prone. (Even
+ %% more so than sleep!)
+ test_server:sleep(?SLEEP)
+ end.
+
diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk
index 603c419653..a8966d46d7 100644
--- a/lib/ssl/vsn.mk
+++ b/lib/ssl/vsn.mk
@@ -1,25 +1,16 @@
-#
-# %CopyrightBegin%
-#
-# Copyright Ericsson AB 1999-2009. All Rights Reserved.
-#
-# The contents of this file are subject to the Erlang Public License,
-# Version 1.1, (the "License"); you may not use this file except in
-# 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.
-#
-# %CopyrightEnd%
-#
-
-SSL_VSN = 3.10.7
-
-TICKETS = OTP-8260 OTP-8218 OTP-8250
+SSL_VSN = 3.11
+
+TICKETS = OTP-8517 \
+ OTP-7046 \
+ OTP-8557 \
+ OTP-8560 \
+ OTP-8545 \
+ OTP-8554
+
+#TICKETS_3.10.9 = OTP-8510
+
+#TICKETS_3.10.8 = OTP-8372 OTP-8441 OTP-8459
+#TICKETS_3.10.7 = OTP-8260 OTP-8218 OTP-8250
#TICKETS_3.10.6 = OTP-8275