diff options
Diffstat (limited to 'lib')
48 files changed, 779 insertions, 447 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index d68a78ed6d..72181a42b0 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -94,12 +94,12 @@ in this module.</p> <taglist> -<tag><c>Address()</c></tag> -<tag><c>DiameterIdentity()</c></tag> -<tag><c>Grouped()</c></tag> -<tag><c>OctetString()</c></tag> -<tag><c>Time()</c></tag> -<tag><c>Unsigned32()</c></tag> +<tag><c>Address()</c></tag><item/> +<tag><c>DiameterIdentity()</c></tag><item/> +<tag><c>Grouped()</c></tag><item/> +<tag><c>OctetString()</c></tag><item/> +<tag><c>Time()</c></tag><item/> +<tag><c>Unsigned32()</c></tag><item/> <tag><c>UTF8String()</c></tag> <item> <p> @@ -159,8 +159,7 @@ Has one the following types.</p> <p> Unique identifier for the application in the scope of the service. -Defaults to the value of the <c>dictionary</c> option if -unspecified.</p> +Defaults to the value of the <c>dictionary</c> option.</p> </item> <tag><c>{dictionary, atom()}</c></tag> @@ -187,7 +186,7 @@ Initial callback state. The prevailing state is passed to some &man_app; callbacks, which can then return a new state. -Defaults to the value of the <c>alias</c> option if unspecified.</p> +Defaults to the value of the <c>alias</c> option.</p> </item> <tag><c>{call_mutates_state, true|false}</c></tag> @@ -195,7 +194,7 @@ Defaults to the value of the <c>alias</c> option if unspecified.</p> <p> Whether or not the &app_pick_peer; application callback can modify the application state. -Defaults to <c>false</c> if unspecified.</p> +Defaults to <c>false</c>.</p> <warning> <p> @@ -228,7 +227,7 @@ question is as if a callback had taken place and returned <c>{error, failure}</c>.</p> <p> -Defaults to <c>discard</c> if unspecified.</p> +Defaults to <c>discard</c>.</p> </item> <tag><c>{request_errors, answer_3xxx|answer|callback}</c></tag> @@ -249,7 +248,7 @@ place and its return value determines the answer sent to the peer, if any.</p> <p> -Defaults to <c>answer_3xxx</c> if unspecified.</p> +Defaults to <c>answer_3xxx</c>.</p> <note> <p> @@ -339,8 +338,8 @@ Has one of the following types.</p> <taglist> -<tag><c>{'Origin-Host', &dict_DiameterIdentity;}</c></tag> -<tag><c>{'Origin-Realm', &dict_DiameterIdentity;}</c></tag> +<tag><c>{'Origin-Host', &dict_DiameterIdentity;}</c></tag><item/> +<tag><c>{'Origin-Realm', &dict_DiameterIdentity;}</c></tag><item/> <tag><c>{'Host-IP-Address', [&dict_Address;]}</c></tag> <item> <p> @@ -352,8 +351,8 @@ question communicates an address list as described in &man_transport;</p> </item> -<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag> -<tag><c>{'Product-Name', &dict_UTF8String;}</c></tag> +<tag><c>{'Vendor-Id', &dict_Unsigned32;}</c></tag><item/> +<tag><c>{'Product-Name', &dict_UTF8String;}</c></tag><item/> <tag><c>{'Origin-State-Id', &dict_Unsigned32;}</c></tag> <item> <p> @@ -366,8 +365,8 @@ can be used as to retrieve a value that is computed when the diameter application is started.</p> </item> -<tag><c>{'Supported-Vendor-Id', [&dict_Unsigned32;]}</c></tag> -<tag><c>{'Auth-Application-Id', [&dict_Unsigned32;]}</c></tag> +<tag><c>{'Supported-Vendor-Id', [&dict_Unsigned32;]}</c></tag><item/> +<tag><c>{'Auth-Application-Id', [&dict_Unsigned32;]}</c></tag><item/> <tag><c>{'Inband-Security-Id', [&dict_Unsigned32;]}</c></tag> <item> <p> @@ -377,9 +376,9 @@ If 1 (TLS) is specified then TLS is selected if the CER/CEA received from the peer offers it.</p> </item> -<tag><c>{'Acct-Application-Id', [&dict_Unsigned32;]}</c></tag> -<tag><c>{'Vendor-Specific-Application-Id', [&dict_Grouped;]}</c></tag> -<tag><c>{'Firmware-Revision', &dict_Unsigned32;}</c></tag> +<tag><c>{'Acct-Application-Id', [&dict_Unsigned32;]}</c></tag><item/> +<tag><c>{'Vendor-Specific-Application-Id', [&dict_Grouped;]}</c></tag><item/> +<tag><c>{'Firmware-Revision', &dict_Unsigned32;}</c></tag><item/> </taglist> @@ -567,9 +566,8 @@ Can have one of the following types.</p> <taglist> -<tag><c>start</c></tag> +<tag><c>start</c></tag><item/> <tag><c>stop</c></tag> - <item> <p> The service is being started or stopped. @@ -578,8 +576,8 @@ No event follows a <c>stop</c> event, and this event implies the termination of all transport processes.</p> </item> -<tag><c>{up, Ref, Peer, Config, Pkt}</c></tag> -<tag><c>{up, Ref, Peer, Config}</c></tag> +<tag><c>{up, Ref, Peer, Config, Pkt}</c></tag><item/> +<tag><c>{up, Ref, Peer, Config}</c></tag><item/> <tag><c>{down, Ref, Peer, Config}</c></tag> <item> <pre> @@ -788,8 +786,8 @@ be matched by corresponding &capability; configuration, of </item> -<marker id="incoming_maxlen"/> -<tag><c>{incoming_maxlen, 0..16777215}</c></tag> +<tag> +<marker id="incoming_maxlen"/><c>{incoming_maxlen, 0..16777215}</c></tag> <item> <p> Bound on the expected size of incoming Diameter messages. @@ -917,8 +915,8 @@ Options <c>monitor</c> and <c>link</c> are ignored.</p> Defaults to the empty list.</p> </item> -<marker id="strict_mbit"/> -<tag><c>{strict_mbit, boolean()}</c></tag> +<tag> +<marker id="strict_mbit"/><c>{strict_mbit, boolean()}</c></tag> <item> <p> Whether or not to regard an AVP setting the M-bit as erroneous when @@ -935,7 +933,7 @@ Defaults to <c>true</c>.</p> <p> RFC 6733 is unclear about the semantics of the M-bit. One the one hand, the CCF specification in section 3.2 documents AVP -in a command grammar as meaning <b>any</b> arbitrary AVP; on the +in a command grammar as meaning <em>any</em> arbitrary AVP; on the other hand, 1.3.4 states that AVPs setting the M-bit cannot be added to an existing command: the modified command must instead be placed in a new Diameter application.</p> @@ -945,7 +943,7 @@ allowing arbitrary AVPs setting the M-bit in a command makes its interpretation implementation-dependent, since there's no guarantee that all implementations will understand the same set of arbitrary AVPs in the context of a given command. -However, interpreting <c>AVP</c> in a command grammar as <b>any</b> +However, interpreting <c>AVP</c> in a command grammar as any AVP, regardless of M-bit, renders 1.3.4 meaningless, since the receiver can simply ignore any AVP it thinks isn't relevant, regardless of the sender's intent.</p> @@ -960,8 +958,8 @@ occur in the message in question.</p> </item> -<marker id="string_decode"/> -<tag><c>{string_decode, boolean()}</c></tag> +<tag> +<marker id="string_decode"/><c>{string_decode, boolean()}</c></tag> <item> <p> Whether or not to decode AVPs of type &dict_OctetString; and its @@ -1031,8 +1029,9 @@ Option passed to &add_transport;. Has one of the following types.</p> <taglist> -<marker id="applications"/> -<tag><c>{applications, [&application_alias;]}</c></tag> + +<tag> +<marker id="applications"/><c>{applications, [&application_alias;]}</c></tag> <item> <p> Diameter applications to which the transport should be restricted. @@ -1050,8 +1049,8 @@ implies having to set matching *-Application-Id AVPs in a </item> -<marker id="capabilities"/> -<tag><c>{capabilities, [&capability;]}</c></tag> +<tag> +<marker id="capabilities"/><c>{capabilities, [&capability;]}</c></tag> <item> <p> AVPs used to construct outgoing CER/CEA messages. @@ -1064,8 +1063,8 @@ may be particularly appropriate for Inband-Security-Id, in case TLS is desired over TCP as implemented by &man_tcp;.</p> </item> -<marker id="capabilities_cb"/> -<tag><c>{capabilities_cb, &evaluable;}</c></tag> +<tag> +<marker id="capabilities_cb"/><c>{capabilities_cb, &evaluable;}</c></tag> <item> <p> Callback invoked upon reception of CER/CEA during capabilities @@ -1112,8 +1111,8 @@ case the corresponding callbacks are applied until either all return <c>ok</c> or one does not.</p> </item> -<marker id="capx_timeout"/> -<tag><c>{capx_timeout, &dict_Unsigned32;}</c></tag> +<tag> +<marker id="capx_timeout"/><c>{capx_timeout, &dict_Unsigned32;}</c></tag> <item> <p> Number of milliseconds after which a transport process having an @@ -1127,8 +1126,8 @@ For a listening transport, the peer determines the timing.</p> Defaults to 10000.</p> </item> -<marker id="connect_timer"/> -<tag><c>{connect_timer, Tc}</c></tag> +<tag> +<marker id="connect_timer"/><c>{connect_timer, Tc}</c></tag> <item> <pre> Tc = &dict_Unsigned32; @@ -1158,9 +1157,8 @@ Defaults to 30000 for a connecting transport and 60000 for a listening transport.</p> </item> -<marker id="disconnect_cb"/> -<tag><c>{disconnect_cb, &evaluable;}</c></tag> - +<tag> +<marker id="disconnect_cb"/><c>{disconnect_cb, &evaluable;}</c></tag> <item> <p> Callback invoked prior to terminating the transport process of a @@ -1236,8 +1234,8 @@ configured them.</p> Defaults to a single callback returning <c>dpr</c>.</p> </item> -<marker id="dpa_timeout"/> -<tag><c>{dpa_timeout, &dict_Unsigned32;}</c></tag> +<tag> +<marker id="dpa_timeout"/><c>{dpa_timeout, &dict_Unsigned32;}</c></tag> <item> <p> Number of milliseconds after which a transport connection is @@ -1247,8 +1245,8 @@ terminated following an outgoing DPR if DPA is not received.</p> Defaults to 1000.</p> </item> -<marker id="dpr_timeout"/> -<tag><c>{dpr_timeout, &dict_Unsigned32;}</c></tag> +<tag> +<marker id="dpr_timeout"/><c>{dpr_timeout, &dict_Unsigned32;}</c></tag> <item> <p> Number of milliseconds after which a transport connection is @@ -1259,8 +1257,8 @@ connection.</p> Defaults to 5000.</p> </item> -<marker id="length_errors"/> -<tag><c>{length_errors, exit|handle|discard}</c></tag> +<tag> +<marker id="length_errors"/><c>{length_errors, exit|handle|discard}</c></tag> <item> <p> How to deal with errors in the Message Length field of the @@ -1308,8 +1306,8 @@ the same peer.</p> </item> -<marker id="spawn_opt"/> -<tag><c>{spawn_opt, [term()]}</c></tag> +<tag> +<marker id="spawn_opt"/><c>{spawn_opt, [term()]}</c></tag> <item> <p> Options passed to &spawn_opt; when spawning a process for an @@ -1320,15 +1318,15 @@ Options <c>monitor</c> and <c>link</c> are ignored.</p> Defaults to the list configured on the service if not specified.</p> </item> -<marker id="transport_config"/> -<tag><c>{transport_config, term()}</c></tag> +<tag> +<marker id="transport_config"/><c>{transport_config, term()}</c></tag><item/> <tag><c>{transport_config, term(), &dict_Unsigned32; | infinity}</c></tag> <item> <p> Term passed as the third argument to the &transport_start; function of the relevant &transport_module; in order to start a transport process. -Defaults to the empty list if unspecified.</p> +Defaults to the empty list.</p> <p> The 3-tuple form additionally specifies an interval, in milliseconds, @@ -1349,12 +1347,12 @@ request a connection with one peer over SCTP or another To listen on both SCTP and TCP, define one transport for each.</p> </item> -<marker id="transport_module"/> -<tag><c>{transport_module, atom()}</c></tag> +<tag> +<marker id="transport_module"/><c>{transport_module, atom()}</c></tag> <item> <p> Module implementing a transport process as defined in &man_transport;. -Defaults to <c>diameter_tcp</c> if unspecified.</p> +Defaults to <c>diameter_tcp</c>.</p> <p> Multiple <c>transport_module</c> and &transport_config; @@ -1369,8 +1367,9 @@ modules in order until one establishes a connection within the corresponding timeout (see below) or all fail.</p> </item> -<marker id="watchdog_config"/> -<tag><c>{watchdog_config, [{okay|suspect, non_neg_integer()}]}</c></tag> +<tag> +<marker id="watchdog_config"/><c>{watchdog_config, + [{okay|suspect, non_neg_integer()}]}</c></tag> <item> <p> Configuration that alters the behaviour of the watchdog @@ -1393,8 +1392,8 @@ misbehaving nodes during test.</p> </warning> </item> -<marker id="watchdog_timer"/> -<tag><c>{watchdog_timer, TwInit}</c></tag> +<tag> +<marker id="watchdog_timer"/><c>{watchdog_timer, TwInit}</c></tag> <item> <pre> TwInit = &dict_Unsigned32; @@ -1412,7 +1411,7 @@ the callback.</p> <p> An integer value must be at least 6000 as required by RFC 3539. -Defaults to 30000 if unspecified.</p> +Defaults to 30000.</p> </item> </taglist> @@ -1422,10 +1421,10 @@ Unrecognized options are silently ignored but are returned unmodified by &service_info; and can be referred to in predicate functions passed to &remove_transport;.</p> -<marker id="transport_ref"/> </item> -<tag><c>transport_ref() = reference()</c></tag> +<tag> +<marker id="transport_ref"/><c>transport_ref() = reference()</c></tag> <item> <p> Reference returned by &add_transport; that @@ -1682,17 +1681,17 @@ returned.</p> <taglist> -<tag><c>'Origin-Host'</c></tag> -<tag><c>'Origin-Realm'</c></tag> -<tag><c>'Vendor-Id'</c></tag> -<tag><c>'Product-Name'</c></tag> -<tag><c>'Origin-State-Id'</c></tag> -<tag><c>'Host-IP-Address'</c></tag> -<tag><c>'Supported-Vendor'</c></tag> -<tag><c>'Auth-Application-Id'</c></tag> -<tag><c>'Inband-Security-Id'</c></tag> -<tag><c>'Acct-Application-Id'</c></tag> -<tag><c>'Vendor-Specific-Application-Id'</c></tag> +<tag><c>'Origin-Host'</c></tag><item/> +<tag><c>'Origin-Realm'</c></tag><item/> +<tag><c>'Vendor-Id'</c></tag><item/> +<tag><c>'Product-Name'</c></tag><item/> +<tag><c>'Origin-State-Id'</c></tag><item/> +<tag><c>'Host-IP-Address'</c></tag><item/> +<tag><c>'Supported-Vendor'</c></tag><item/> +<tag><c>'Auth-Application-Id'</c></tag><item/> +<tag><c>'Inband-Security-Id'</c></tag><item/> +<tag><c>'Acct-Application-Id'</c></tag><item/> +<tag><c>'Vendor-Specific-Application-Id'</c></tag><item/> <tag><c>'Firmware-Revision'</c></tag> <item> <p> diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml index 973b6eb732..dfcd00975b 100644 --- a/lib/diameter/doc/src/diameter_app.xml +++ b/lib/diameter/doc/src/diameter_app.xml @@ -90,17 +90,14 @@ is called in response to an incoming Diameter request message.</p> </list> -</description> - -<note> <p> -The arities given for the the callback functions here assume no extra -arguments. +The arities for the the callback functions here assume no extra arguments. All functions will also be passed any extra arguments configured with the callback module itself when calling &mod_start_service; and, for the call-specific callbacks, any extra arguments passed to &mod_call;.</p> -</note> + +</description> <!-- ===================================================================== --> <!-- ===================================================================== --> @@ -110,9 +107,8 @@ and, for the call-specific callbacks, any extra arguments passed to <taglist> -<marker id="capabilities"/> - -<tag><c>capabilities() = #diameter_caps{}</c></tag> +<tag> +<marker id="capabilities"/><c>capabilities() = #diameter_caps{}</c></tag> <item> <p> A record containing the identities of @@ -126,9 +122,8 @@ Optional or possibly multiple values are encoded as lists of values, mandatory values as the bare value.</p> </item> -<marker id="message"/> - -<tag><c>message() = &codec_message;</c></tag> +<tag> +<marker id="message"/><c>message() = &codec_message;</c></tag> <item> <p> The representation of a Diameter message as passed to @@ -136,9 +131,8 @@ The representation of a Diameter message as passed to </item> -<marker id="packet"/> - -<tag><c>packet() = &codec_packet;</c></tag> +<tag> +<marker id="packet"/><c>packet() = &codec_packet;</c></tag> <item> <p> A container for incoming and outgoing Diameter messages that's passed @@ -146,25 +140,22 @@ through encode/decode and transport. Fields should not be set in return values except as documented.</p> </item> -<marker id="peer_ref"/> - -<tag><c>peer_ref() = term()</c></tag> +<tag> +<marker id="peer_ref"/><c>peer_ref() = term()</c></tag> <item> <p> A term identifying a transport connection with a Diameter peer.</p> </item> -<marker id="peer"/> - -<tag><c>peer() = {&peer_ref;, &capabilities;}</c></tag> +<tag> +<marker id="peer"/><c>peer() = {&peer_ref;, &capabilities;}</c></tag> <item> <p> A tuple representing a Diameter peer connection.</p> </item> -<marker id="state"/> - -<tag><c>state() = term()</c></tag> +<tag> +<marker id="state"/><c>state() = term()</c></tag> <item> <p> The state maintained by the application callback functions diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml index a0313e2877..91e96058dd 100644 --- a/lib/diameter/doc/src/diameter_codec.xml +++ b/lib/diameter/doc/src/diameter_codec.xml @@ -88,10 +88,9 @@ files resulting from dictionary file compilation.</p> <taglist> -<marker id="integers"/> - -<tag><c>uint8() = 0..255</c></tag> -<tag><c>uint24() = 0..16777215</c></tag> +<tag> +<marker id="integers"/><c>uint8() = 0..255</c></tag><item/> +<tag><c>uint24() = 0..16777215</c></tag><item/> <tag><c>uint32() = 0..4294967295</c></tag> <item> <p> @@ -99,9 +98,8 @@ files resulting from dictionary file compilation.</p> headers.</p> </item> -<marker id="avp"/> - -<tag><c>avp() = #diameter_avp{}</c></tag> +<tag> +<marker id="avp"/><c>avp() = #diameter_avp{}</c></tag> <item> <p> The application-neutral representation of an AVP. @@ -116,9 +114,9 @@ Fields have the following types.</p> <taglist> -<tag><c>code = uint32()</c></tag> -<tag><c>is_mandatory = boolean()</c></tag> -<tag><c>need_encryption = boolean()</c></tag> +<tag><c>code = uint32()</c></tag><item/> +<tag><c>is_mandatory = boolean()</c></tag><item/> +<tag><c>need_encryption = boolean()</c></tag><item/> <tag><c>vendor_id = uint32() | undefined</c></tag> <item> <p> @@ -167,9 +165,8 @@ Possible types are <c>undefined</c> and the Diameter types: </item> -<marker id="dictionary"/> - -<tag><c>dictionary() = module()</c></tag> +<tag> +<marker id="dictionary"/><c>dictionary() = module()</c></tag> <item> <p> @@ -179,9 +176,8 @@ The interface provided by a dictionary module is an implementation detail that may change.</p> </item> -<marker id="header"/> - -<tag><c>header() = #diameter_header{}</c></tag> +<tag> +<marker id="header"/><c>header() = #diameter_header{}</c></tag> <item> <p> The record representation of the Diameter header. @@ -204,11 +200,11 @@ Fields have the following types.</p> <taglist> -<tag><c>version = uint8()</c></tag> -<tag><c>length = uint24()</c></tag> -<tag><c>cmd_code = uint24()</c></tag> -<tag><c>application_id = uint32()</c></tag> -<tag><c>hop_by_hop_id = uint32()</c></tag> +<tag><c>version = uint8()</c></tag><item/> +<tag><c>length = uint24()</c></tag><item/> +<tag><c>cmd_code = uint24()</c></tag><item/> +<tag><c>application_id = uint32()</c></tag><item/> +<tag><c>hop_by_hop_id = uint32()</c></tag><item/> <tag><c>end_to_end_id = uint32()</c></tag> <item> <p> @@ -217,9 +213,9 @@ Hop-by-Hop Identifier and End-to-End Identifier fields of the Diameter header.</p> </item> -<tag><c>is_request = boolean()</c></tag> -<tag><c>is_proxiable = boolean()</c></tag> -<tag><c>is_error = boolean()</c></tag> +<tag><c>is_request = boolean()</c></tag><item/> +<tag><c>is_proxiable = boolean()</c></tag><item/> +<tag><c>is_error = boolean()</c></tag><item/> <tag><c>is_retransmitted = boolean()</c></tag> <item> <p> @@ -232,9 +228,8 @@ header.</p> </item> -<marker id="message"/> - -<tag><c>message() = record() | list()</c></tag> +<tag> +<marker id="message"/><c>message() = record() | list()</c></tag> <item> <p> The representation of a Diameter message as passed to @@ -257,9 +252,8 @@ question: messages are sent exactly as specified.</p> </item> -<marker id="packet"/> - -<tag><c>packet() = #diameter_packet{}</c></tag> +<tag> +<marker id="packet"/><c>packet() = #diameter_packet{}</c></tag> <item> <p> A container for incoming and outgoing Diameter messages. @@ -296,7 +290,7 @@ corresponding values.</p> <warning> <p> -A record-valued <c>msg</c> field does <b>not</b> imply an absence of +A record-valued <c>msg</c> field does <em>not</em> imply an absence of decode errors. The <c>errors</c> field should also be examined.</p> </warning> diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml index ae40f99aee..9584d682c2 100644 --- a/lib/diameter/doc/src/diameter_dict.xml +++ b/lib/diameter/doc/src/diameter_dict.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8" ?> -<!DOCTYPE erlref SYSTEM "fileref.dtd" [ +<!DOCTYPE fileref SYSTEM "fileref.dtd" [ <!ENTITY format '<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso>'> <!ENTITY records @@ -121,9 +121,8 @@ The order in which sections are specified is unimportant.</p> <taglist> -<marker id="id"/> - -<tag><c>@id Number</c></tag> +<tag> +<marker id="id"/><c>@id Number</c></tag> <item> <p> Defines the integer Number as the Diameter Application Id of the @@ -146,14 +145,13 @@ Example:</p> </item> -<marker id="name"/> - -<tag><c>@name Mod</c></tag> +<tag> +<marker id="name"/><c>@name Mod</c></tag> <item> <p> Defines the name of the generated dictionary module. Can occur at most once and defaults to the name of the dictionary file -minus any extension if unspecified. +minus any extension. The section has empty content.</p> <p> @@ -169,9 +167,8 @@ Example:</p> </item> -<marker id="prefix"/> - -<tag><c>@prefix Name</c></tag> +<tag> +<marker id="prefix"/><c>@prefix Name</c></tag> <item> <p> Defines Name as the prefix to be added to record and constant names @@ -194,9 +191,8 @@ Example:</p> </item> -<marker id="vendor"/> - -<tag><c>@vendor Number Name</c></tag> +<tag> +<marker id="vendor"/><c>@vendor Number Name</c></tag> <item> <p> Defines the integer Number as the the default Vendor-Id of AVPs for @@ -216,9 +212,8 @@ Example:</p> </item> -<marker id="avp_vendor_id"/> - -<tag><c>@avp_vendor_id Number</c></tag> +<tag> +<marker id="avp_vendor_id"/><c>@avp_vendor_id Number</c></tag> <item> <p> Defines the integer Number as the Vendor-Id of the AVPs listed in the @@ -238,9 +233,8 @@ Region-Set </item> -<marker id="inherits"/> - -<tag><c>@inherits Mod</c></tag> +<tag> +<marker id="inherits"/><c>@inherits Mod</c></tag> <item> <p> Defines the name of a dictionary module containing AVP @@ -274,9 +268,8 @@ Example:</p> </pre> </item> -<marker id="avp_types"/> - -<tag><c>@avp_types</c></tag> +<tag> +<marker id="avp_types"/><c>@avp_types</c></tag> <item> <p> Defines the name, code, type and flags of individual AVPs. @@ -308,9 +301,8 @@ The P flag has been deprecated by &the_rfc;.</p> </item> -<marker id="custom_types"/> - -<tag><c>@custom_types Mod</c></tag> +<tag> +<marker id="custom_types"/><c>@custom_types Mod</c></tag> <item> <p> Specifies AVPs for which module Mod provides encode/decode functions. @@ -331,9 +323,8 @@ Framed-IP-Address </pre> </item> -<marker id="codecs"/> - -<tag><c>@codecs Mod</c></tag> +<tag> +<marker id="codecs"/><c>@codecs Mod</c></tag> <item> <p> Like <c>@custom_types</c> but requires the specified module to export @@ -350,9 +341,8 @@ Framed-IP-Address </pre> </item> -<marker id="messages"/> - -<tag><c>@messages</c></tag> +<tag> +<marker id="messages"/><c>@messages</c></tag> <item> <p> Defines the messages of the application. @@ -397,9 +387,8 @@ RTA ::= < Diameter Header: 287, PXY > </item> -<marker id="grouped"/> - -<tag><c>@grouped</c></tag> +<tag> +<marker id="grouped"/><c>@grouped</c></tag> <item> <p> Defines the contents of the AVPs of the application having type @@ -424,9 +413,8 @@ Specifying a Vendor-Id in the definition of a grouped AVP is equivalent to specifying it with <c>@avp_vendor_id</c>.</p> </item> -<marker id="enum"/> - -<tag><c>@enum Name</c></tag> +<tag> +<marker id="enum"/><c>@enum Name</c></tag> <item> <p> Defines values of AVP Name having type Enumerated. @@ -452,9 +440,8 @@ REMOVE_SIP_SERVER 3 </pre> </item> -<marker id="end"/> - -<tag><c>@end</c></tag> +<tag> +<marker id="end"/><c>@end</c></tag> <item> <p> Causes parsing of the dictionary to terminate: diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml index 853ef96bb3..2e1f2b3c03 100644 --- a/lib/diameter/doc/src/diameter_examples.xml +++ b/lib/diameter/doc/src/diameter_examples.xml @@ -42,4 +42,3 @@ Example code can be found in the diameter application's <c>examples</c> subdirectory.</p> </chapter> - diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml index 00ccc39c15..6ca280c52b 100644 --- a/lib/diameter/doc/src/diameter_tcp.xml +++ b/lib/diameter/doc/src/diameter_tcp.xml @@ -150,7 +150,7 @@ Options <c>binary</c>, <c>packet</c> and <c>active</c> cannot be specified. Also, option <c>port</c> can be specified for a listening transport to specify the local listening port, the default being the standardized -3868 if unspecified. +3868. Note that the option <c>ip</c> specifies the local address.</p> <p> diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml index 736d4cbfbd..294e8a8864 100644 --- a/lib/diameter/doc/src/diameter_transport.xml +++ b/lib/diameter/doc/src/diameter_transport.xml @@ -65,9 +65,8 @@ parent).</p> <taglist> -<marker id="message"/> - -<tag><c>message() = binary() | &codec_packet;</c></tag> +<tag> +<marker id="message"/><c>message() = binary() | &codec_packet;</c></tag> <item> <p> A Diameter message as passed over the transport interface.</p> @@ -160,9 +159,9 @@ It should exit if its transport connection with its peer is lost.</p> </funcs> <!-- ===================================================================== --> -<marker id="MESSAGES"/> <section> +<marker id="MESSAGES"/> <title>MESSAGES</title> <p> @@ -234,7 +233,7 @@ established a connection with the peer. Not sent if the transport process has <c>Type=connect</c>.</p> </item> -<tag><c>{diameter, {self(), connected, Remote}}</c></tag> +<tag><c>{diameter, {self(), connected, Remote}}</c></tag><item/> <tag><c>{diameter, {self(), connected, Remote, [LocalAddr]}}</c></tag> <item> <p> diff --git a/lib/diameter/doc/src/diameterc.xml b/lib/diameter/doc/src/diameterc.xml index 5bffe9a771..8f1c660989 100644 --- a/lib/diameter/doc/src/diameterc.xml +++ b/lib/diameter/doc/src/diameterc.xml @@ -11,7 +11,7 @@ <comref> <header> <copyright> -<year>2011</year><year>2013</year> +<year>2011</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -79,14 +79,14 @@ Write generated source to the specified directory. Defaults to the current working directory.</p> </item> -<tag><![CDATA[-E]]></tag> +<tag><![CDATA[-E]]></tag><item/> <tag><![CDATA[-H]]></tag> <item> <p> Suppress erl and hrl generation, respectively.</p> </item> -<tag><![CDATA[--name <name>]]></tag> +<tag><![CDATA[--name <name>]]></tag><item/> <tag><![CDATA[--prefix <prefix>]]></tag> <item> <p> diff --git a/lib/diameter/examples/code/relay.erl b/lib/diameter/examples/code/relay.erl index 3846b1d161..cf4ce8848b 100644 --- a/lib/diameter/examples/code/relay.erl +++ b/lib/diameter/examples/code/relay.erl @@ -53,7 +53,7 @@ {'Auth-Application-Id', [16#FFFFFFFF]}, {string_decode, false}, {application, [{alias, relay}, - {dictionary, diameter_relay}, + {dictionary, diameter_gen_relay}, {module, relay_cb}]}]). %% start/1 diff --git a/lib/diameter/src/base/diameter_lib.erl b/lib/diameter/src/base/diameter_lib.erl index b835e87967..3928769b5e 100644 --- a/lib/diameter/src/base/diameter_lib.erl +++ b/lib/diameter/src/base/diameter_lib.erl @@ -299,8 +299,28 @@ spawn_opts(server, Opts) -> spawn_opts(worker, Opts) -> opts(5000, Opts). -opts(HeapSize, Opts) -> - [{min_heap_size, HeapSize} | lists:keydelete(min_heap_size, 1, Opts)]. +%% These setting are historical rather than useful. In particular, the +%% server setting can bloat many processes unnecessarily. Let them be +%% disabled with -diameter min_heap_size false. + +opts(Def, Opts) -> + Key = min_heap_size, + case getenv(Key, Def) of + N when is_integer(N), 0 =< N -> + [{Key, N} | lists:keydelete(Key, 1, Opts)]; + _ -> + Opts + end. + +%% getenv/1 + +getenv(Key, Def) -> + case application:get_env(Key) of + {ok, T} -> + T; + undefined -> + Def + end. %% --------------------------------------------------------------------------- %% # wait/1 diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 2112941d5e..d93a3e71e3 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -123,8 +123,16 @@ peer_up(TPid) -> %% --------------------------------------------------------------------------- peer_down(TPid) -> - ets:delete(?REQUEST_TABLE, TPid), - failover(TPid). + ets:delete_object(?REQUEST_TABLE, {TPid}), + lists:foreach(fun failover/1, ets:lookup(?REQUEST_TABLE, TPid)). +%% Note that a request process can store its request after failover +%% notifications are sent here: insert_request/2 sends the notification +%% in that case. + +%% failover/1 + +failover({_TPid, {Pid, TRef}}) -> + Pid ! {failover, TRef}. %% --------------------------------------------------------------------------- %% incr/4 @@ -911,7 +919,7 @@ failed(Rec, FailedAvp, Dict) -> {'Failed-AVP', [FailedAvp]} catch error: _ -> - Avps = Dict:'get-'('AVP', Rec), + Avps = Dict:'#get-'('AVP', Rec), A = #diameter_avp{name = 'Failed-AVP', value = FailedAvp}, {'AVP', [A|Avps]} @@ -1452,7 +1460,7 @@ make_request_packet(#diameter_packet{header = Hdr} = Pkt, make_request_packet(Msg, Pkt) -> Pkt#diameter_packet{msg = Msg}. -%% make_retransmit_packet/2 +%% make_retransmit_packet/1 make_retransmit_packet(#diameter_packet{msg = [#diameter_header{} = Hdr | Avps]} @@ -1703,16 +1711,13 @@ send_request(TPid, #diameter_packet{bin = Bin} = Pkt, Req, _SvcName, Timeout) when node() == node(TPid) -> Seqs = diameter_codec:sequence_numbers(Bin), TRef = erlang:start_timer(Timeout, self(), TPid), - Entry = {Seqs, Req, TRef}, + Entry = {Seqs, #request{handler = Pid} = Req, TRef}, - %% Ensure that request table is cleaned even if we receive an exit - %% signal. An alternative would be to simply trap exits, but - %% callbacks are applied in this process, and these could possibly - %% be expecting the prevailing behaviour. - Self = self(), - spawn(fun() -> diameter_lib:wait([Self]), erase_request(Entry) end), + %% Ensure that request table is cleaned even if the process is + %% killed. + spawn(fun() -> diameter_lib:wait([Pid]), delete_request(Entry) end), - store_request(Entry, TPid), + insert_request(Entry), send(TPid, Pkt), TRef; @@ -1774,6 +1779,8 @@ retransmit({TPid, Caps, App} SvcName, Timeout, []). +%% When sending a binary, it's up to prepare_retransmit to modify it +%% accordingly. retransmit({send, Msg}, Transport, @@ -1818,15 +1825,21 @@ resend_request(Pkt0, TRef = send_request(TPid, Pkt, Req, SvcName, Tmo), {TRef, Req}. -%% store_request/2 +%% insert_request/1 -store_request(T, TPid) -> - ets:insert(?REQUEST_TABLE, T), - ets:member(?REQUEST_TABLE, TPid) - orelse begin - {_Seqs, _Req, TRef} = T, - self() ! {failover, TRef} %% failover/1 may have missed - end. +insert_request({_Seqs, #request{transport = TPid}, TRef} = T) -> + ets:insert(?REQUEST_TABLE, [T, {TPid, {self(), TRef}}]), + is_peer_up(TPid) + orelse (self() ! {failover, TRef}). %% failover/1 may have missed + +%% is_peer_up/1 +%% +%% Is the entry written by peer_up/1 and deleted by peer_down/1 still +%% in the request table? + +is_peer_up(TPid) -> + Spec = [{{TPid}, [], ['$_']}], + '$end_of_table' /= ets:select(?REQUEST_TABLE, Spec, 1). %% lookup_request/2 %% @@ -1846,16 +1859,11 @@ lookup_request(Msg, TPid) -> false end. -%% erase_request/1 - -erase_request(T) -> - ets:delete_object(?REQUEST_TABLE, T). - -%% match_requests/1 +%% delete_request/1 -match_requests(TPid) -> - Pat = {'_', #request{transport = TPid, _ = '_'}, '_'}, - ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}]). +delete_request({_Seqs, #request{handler = Pid, transport = TPid}, TRef} = T) -> + Spec = [{R, [], [true]} || R <- [T, {TPid, {Pid, TRef}}]], + ets:select_delete(?REQUEST_TABLE, Spec). %% have_request/2 @@ -1864,28 +1872,6 @@ have_request(Pkt, TPid) -> Pat = {Seqs, #request{transport = TPid, _ = '_'}, '_'}, '$end_of_table' /= ets:select(?REQUEST_TABLE, [{Pat, [], ['$_']}], 1). -%% --------------------------------------------------------------------------- -%% # failover/1-2 -%% --------------------------------------------------------------------------- - -failover(TPid) - when is_pid(TPid) -> - lists:foreach(fun failover/1, match_requests(TPid)); -%% Note that a request process can store its request after failover -%% notifications are sent here: store_request/2 sends the notification -%% in that case. - -%% Failover as a consequence of peer_down/1: inform the -%% request process. -failover({_, Req, TRef}) -> - #request{handler = Pid, - packet = #diameter_packet{msg = M}} - = Req, - M /= undefined andalso (Pid ! {failover, TRef}). -%% Failover is not performed when msg = binary() since sending -%% pre-encoded binaries is only partially supported. (Mostly for -%% test.) - %% get_destination/2 get_destination(Dict, Msg) -> diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index cdaa9aa7f9..4007d6b7b1 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -790,20 +790,7 @@ header() -> ("%% -------------------------------------------------------------------\n" "%% This is a generated file.\n" "%% -------------------------------------------------------------------\n" - "\n" - "%%\n" - "%% Copyright (c) Ericsson AB. All rights reserved.\n" - "%%\n" - "%% The information in this document is the property of Ericsson.\n" - "%%\n" - "%% Except as specifically authorized in writing by Ericsson, the\n" - "%% receiver of this document shall keep the information contained\n" - "%% herein confidential and shall protect the same in whole or in\n" - "%% part from disclosure and dissemination to third parties.\n" - "%%\n" - "%% Disclosure and disseminations to the receivers employees shall\n" - "%% only be made on a strict need to know basis.\n" - "%%\n\n"). + "\n"). hrl_header(Name) -> header() ++ "-hrl_name('" ++ ?S(Name) ++ ".hrl').\n". diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src index 618d5a7f10..b1b8e38d39 100644 --- a/lib/diameter/src/diameter.appup.src +++ b/lib/diameter/src/diameter.appup.src @@ -49,7 +49,11 @@ {"1.10", [{restart_application, diameter}]}, %% 18.0 {"1.11", [{restart_application, diameter}]}, %% 18.1 {"1.11.1", [{restart_application, diameter}]}, %% 18.2 - {"1.11.2", [{restart_application, diameter}]} %% 18.3 + {"1.11.2", [{restart_application, diameter}]}, %% 18.3 + {"1.12", [{load_module, diameter_lib}, %% 19.0 + {load_module, diameter_traffic}, + {load_module, diameter_tcp}, + {load_module, diameter_sctp}]} ], [ {"0.9", [{restart_application, diameter}]}, @@ -80,6 +84,10 @@ {"1.10", [{restart_application, diameter}]}, {"1.11", [{restart_application, diameter}]}, {"1.11.1", [{restart_application, diameter}]}, - {"1.11.2", [{restart_application, diameter}]} + {"1.11.2", [{restart_application, diameter}]}, + {"1.12", [{load_module, diameter_sctp}, + {load_module, diameter_tcp}, + {load_module, diameter_traffic}, + {load_module, diameter_lib}]} ] }. diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 4a005b853d..f48e4347ee 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -98,7 +98,7 @@ -record(listener, {ref :: reference(), socket :: gen_sctp:sctp_socket(), - count = 0 :: uint(), %% attached transport processes + service = false :: false | pid(), %% service process pending = {0, queue:new()}, accept :: [match()]}). %% Field pending implements two queues: the first of transport-to-be @@ -129,11 +129,14 @@ -> {ok, pid(), [inet:ip_address()]} when Ref :: diameter:transport_ref(). -start(T, #diameter_service{capabilities = Caps}, Opts) +start(T, Svc, Opts) when is_list(Opts) -> + #diameter_service{capabilities = Caps, + pid = SPid} + = Svc, diameter_sctp_sup:start(), %% start supervisors on demand Addrs = Caps#diameter_caps.host_ip_address, - s(T, Addrs, lists:map(fun ip/1, Opts)). + s(T, Addrs, SPid, lists:map(fun ip/1, Opts)). ip({ifaddr, A}) -> {ip, A}; @@ -144,18 +147,22 @@ ip(T) -> %% when there is not yet an association to assign it, or at comm_up on %% a new association in which case the call retrieves a transport from %% the pending queue. -s({accept, Ref} = A, Addrs, Opts) -> - {LPid, LAs} = listener(Ref, {Opts, Addrs}), - try gen_server:call(LPid, {A, self()}, infinity) of - {ok, TPid} -> {ok, TPid, LAs} +s({accept, Ref} = A, Addrs, SPid, Opts) -> + {ok, LPid, LAs} = listener(Ref, {Opts, Addrs}), + try gen_server:call(LPid, {A, self(), SPid}, infinity) of + {ok, TPid} -> + {ok, TPid, LAs}; + No -> + {error, No} catch - exit: Reason -> {error, Reason} + exit: Reason -> + {error, Reason} end; %% This implementation is due to there being no accept call in %% gen_sctp in order to be able to accept a new association only %% *after* an accepting transport has been spawned. -s({connect = C, Ref}, Addrs, Opts) -> +s({connect = C, Ref}, Addrs, _SPid, Opts) -> diameter_sctp_sup:start_child({C, self(), Opts, Addrs, Ref}). %% start_link/1 @@ -281,24 +288,23 @@ i({K, Ref}, #transport{mode = {accept, _}} = S) -> %% Accepting processes can be started concurrently: ensure only one %% listener is started. -listener(LRef, T) -> - diameter_sync:call({?MODULE, listener, LRef}, - {?MODULE, listener, [{LRef, T}]}, +listener(Ref, T) -> + diameter_sync:call({?MODULE, listener, Ref}, + {?MODULE, listener, [{Ref, T}]}, infinity, infinity). -listener({LRef, T}) -> - l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T). +listener({Ref, T}) -> + l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T). %% Existing listening process ... l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) -> - {LAs, _Sock} = AS, - {LPid, LAs}; + {LAs, _Sock} = AS, + {ok, LPid, LAs}; %% ... or not. -l([], LRef, T) -> - {ok, LPid, LAs} = diameter_sctp_sup:start_child({listen, LRef, T}), - {LPid, LAs}. +l([], Ref, T) -> + diameter_sctp_sup:start_child({listen, Ref, T}). %% open/3 @@ -364,11 +370,17 @@ type(T) -> %% # handle_call/3 %% --------------------------------------------------------------------------- -handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref, - count = K} - = S) -> +handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref} = S) -> {TPid, NewS} = accept(Ref, Pid, S), - {reply, {ok, TPid}, NewS#listener{count = K+1}}; + {reply, {ok, TPid}, NewS}; + +handle_call({{accept, _} = T, Pid, SPid}, From, #listener{service = P} = S) -> + handle_call({T, Pid}, From, if not is_pid(P), is_pid(SPid) -> + monitor(process, SPid), + S#listener{service = SPid}; + true -> + S + end); handle_call(_, _, State) -> {reply, nok, State}. @@ -441,6 +453,13 @@ l({sctp, Sock, _RA, _RP, Data} = T, #listener{socket = Sock, setopts(Sock), NewS; +%% Service process has died. +l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid, + socket = Sock}) -> + gen_sctp:close(Sock), + x(T); + +%% Accepting process has died. l({'DOWN', _MRef, process, TPid, _}, #listener{pending = {_,Q}} = S) -> down(queue:member(TPid, Q), TPid, S); @@ -454,20 +473,17 @@ l({transport, remove, _} = T, #listener{socket = Sock}) -> %% Accepting transport has died. %% One that's waiting for transport start in the pending queue ... -down(true, TPid, #listener{pending = {N,Q}, - count = K} - = S) -> +down(true, TPid, #listener{pending = {N,Q}} = S) -> NQ = queue:filter(fun(P) -> P /= TPid end, Q), if N < 0 -> %% awaiting an association ... - S#listener{count = K-1, - pending = {N+1, NQ}}; + S#listener{pending = {N+1, NQ}}; true -> %% ... or one has been assigned S#listener{pending = {N-1, NQ}} end; %% ... or one that's already attached. -down(false, _TPid, #listener{count = K} = S) -> - S#listener{count = K-1}. +down(false, _TPid, S) -> + S. %% t/2 %% diff --git a/lib/diameter/src/transport/diameter_tcp.erl b/lib/diameter/src/transport/diameter_tcp.erl index 546c2cfa5e..44abc5c3b4 100644 --- a/lib/diameter/src/transport/diameter_tcp.erl +++ b/lib/diameter/src/transport/diameter_tcp.erl @@ -71,11 +71,8 @@ %% a process owning the listening port. %% Listener process state. --record(listener, {socket :: inet:socket(), - count = 1 :: non_neg_integer()}). %% accepting processes -%% The count of accepting processes was previously used to terminate -%% the listening process, but diameter_reg:subscribe/2 is now used for -%% this. Leave the the count for trace purposes. +-record(listener, {socket :: inet:socket(), + service = false :: false | pid()}). %% service process %% Monitor process state. -record(monitor, @@ -138,11 +135,15 @@ | {ok, pid()} when Ref :: diameter:transport_ref(). -start({T, Ref}, #diameter_service{capabilities = Caps}, Opts) -> +start({T, Ref}, Svc, Opts) -> + #diameter_service{capabilities = Caps, + pid = SPid} + = Svc, + diameter_tcp_sup:start(), %% start tcp supervisors on demand {Mod, Rest} = split(Opts), Addrs = Caps#diameter_caps.host_ip_address, - Arg = {T, Ref, Mod, self(), Rest, Addrs}, + Arg = {T, Ref, Mod, self(), Rest, Addrs, SPid}, diameter_tcp_sup:start_child(Arg). split([{module, M} | Opts]) -> @@ -196,7 +197,7 @@ init(T) -> %% i/1 %% A transport process. -i({T, Ref, Mod, Pid, Opts, Addrs}) +i({T, Ref, Mod, Pid, Opts, Addrs, SPid}) when T == accept; T == connect -> monitor(process, Pid), @@ -214,7 +215,7 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) ?DEFAULT_FRAGMENT_TIMEOUT), ?IS_TIMEOUT(Tmo) orelse ?ERROR({fragment_timer, Tmo}), Throttle = proplists:get_value(throttle_cb, OwnOpts, false), - Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs), + Sock = init(T, Ref, Mod, Pid, SslOpts, Rest, Addrs, SPid), MPid ! {stop, self()}, %% tell the monitor to die M = if SslOpts -> ssl; true -> Mod end, putr(?REF_KEY, Ref), @@ -228,6 +229,11 @@ i({T, Ref, Mod, Pid, Opts, Addrs}) %% Put the reference in the process dictionary since we now use it %% advertise the ssl socket after TLS upgrade. +i({T, _Ref, _Mod, _Pid, _Opts, _Addrs} = Arg) %% from old code + when T == accept; + T == connect -> + i(erlang:append_element(Arg, _SPid = false)); + %% A monitor process to kill the transport if the parent dies. i(#monitor{parent = Pid, transport = TPid} = S) -> proc_lib:init_ack({ok, self()}), @@ -240,16 +246,18 @@ i(#monitor{parent = Pid, transport = TPid} = S) -> %% death. However, a link can be unlinked and this is exactly what %% gen_tcp seems to so. Links should be left to supervisors. -i({listen, LRef, APid, {Mod, Opts, Addrs}}) -> - [_] = diameter_config:subscribe(LRef, transport), %% assert existence +i({listen = L, Ref, _APid, T}) -> %% from old code + i({L, Ref, T}); + +i({listen, Ref, {Mod, Opts, Addrs}}) -> + [_] = diameter_config:subscribe(Ref, transport), %% assert existence {[LA, LP], Rest} = proplists:split(Opts, [ip, port]), LAddrOpt = get_addr(LA, Addrs), LPort = get_port(LP), {ok, LSock} = Mod:listen(LPort, gen_opts(LAddrOpt, Rest)), LAddr = laddr(LAddrOpt, Mod, LSock), - true = diameter_reg:add_new({?MODULE, listener, {LRef, {LAddr, LSock}}}), + true = diameter_reg:add_new({?MODULE, listener, {Ref, {LAddr, LSock}}}), proc_lib:init_ack({ok, self(), {LAddr, LSock}}), - monitor(process, APid), #listener{socket = LSock}. laddr([], Mod, Sock) -> @@ -268,21 +276,22 @@ ssl_opts([{ssl_options, Opts}]) ssl_opts(T) -> ?ERROR({ssl_options, T}). -%% init/7 +%% init/8 %% Establish a TLS connection before capabilities exchange ... -init(Type, Ref, Mod, Pid, true, Opts, Addrs) -> - init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs); +init(Type, Ref, Mod, Pid, true, Opts, Addrs, SPid) -> + init(Type, Ref, ssl, Pid, [{cb_info, ?TCP_CB(Mod)} | Opts], Addrs, SPid); %% ... or not. -init(Type, Ref, Mod, Pid, _, Opts, Addrs) -> - init(Type, Ref, Mod, Pid, Opts, Addrs). +init(Type, Ref, Mod, Pid, _, Opts, Addrs, SPid) -> + init(Type, Ref, Mod, Pid, Opts, Addrs, SPid). -%% init/6 +%% init/7 -init(accept = T, Ref, Mod, Pid, Opts, Addrs) -> +init(accept = T, Ref, Mod, Pid, Opts, Addrs, SPid) -> {[Matches], Rest} = proplists:split(Opts, [accept]), - {LAddr, LSock} = listener(Ref, {Mod, Rest, Addrs}), + {ok, LPid, {LAddr, LSock}} = listener(Ref, {Mod, Rest, Addrs}), + ok = gen_server:call(LPid, {accept, SPid}, infinity), proc_lib:init_ack({ok, self(), [LAddr]}), Sock = ok(accept(Mod, LSock)), ok = accept_peer(Mod, Sock, accept(Matches)), @@ -290,7 +299,7 @@ init(accept = T, Ref, Mod, Pid, Opts, Addrs) -> diameter_peer:up(Pid), Sock; -init(connect = T, Ref, Mod, Pid, Opts, Addrs) -> +init(connect = T, Ref, Mod, Pid, Opts, Addrs, _SPid) -> {[LA, RA, RP], Rest} = proplists:split(Opts, [ip, raddr, rport]), LAddrOpt = get_addr(LA, Addrs), RAddr = get_addr(RA), @@ -344,24 +353,26 @@ accept(Opts) -> %% Accepting processes can be started concurrently: ensure only one %% listener is started. -listener(LRef, T) -> - diameter_sync:call({?MODULE, listener, LRef}, - {?MODULE, listener, [{LRef, T, self()}]}, +listener(Ref, T) -> + diameter_sync:call({?MODULE, listener, Ref}, + {?MODULE, listener, [{Ref, T, self()}]}, infinity, infinity). -listener({LRef, T, TPid}) -> - l(diameter_reg:match({?MODULE, listener, {LRef, '_'}}), LRef, T, TPid). +%% listener/1 + +listener({Ref, T, _TPid}) -> + l(diameter_reg:match({?MODULE, listener, {Ref, '_'}}), Ref, T). + +%% l/3 %% Existing listening process ... -l([{{?MODULE, listener, {_, AS}}, LPid}], _, _, TPid) -> - LPid ! {accept, TPid}, - AS; +l([{{?MODULE, listener, {_, AS}}, LPid}], _, _) -> + {ok, LPid, AS}; %% ... or not. -l([], LRef, T, TPid) -> - {ok, _, AS} = diameter_tcp_sup:start_child({listen, LRef, TPid, T}), - AS. +l([], Ref, T) -> + diameter_tcp_sup:start_child({listen, Ref, T}). %% get_addr/1 @@ -440,6 +451,14 @@ portnr(Sock) -> %% # handle_call/3 %% --------------------------------------------------------------------------- +handle_call({accept, SPid}, _From, #listener{service = P} = S) -> + {reply, ok, if not is_pid(P), is_pid(SPid) -> + monitor(process, SPid), + S#listener{service = SPid}; + true -> + S + end}; + handle_call(_, _, State) -> {reply, nok, State}. @@ -507,19 +526,20 @@ m({'DOWN', _, process, Pid, _}, #monitor{parent = Pid, %% %% Transition listener state. -%% An accepting transport is attaching. -l({accept, TPid}, #listener{count = N} = S) -> - monitor(process, TPid), - S#listener{count = N+1}; - -%% Accepting process has died. -l({'DOWN', _, process, _, _}, #listener{count = N} = S) -> - S#listener{count = N-1}; +%% Service process has died. +l({'DOWN', _, process, Pid, _} = T, #listener{service = Pid, + socket = Sock}) -> + gen_tcp:close(Sock), + x(T); %% Transport has been removed. l({transport, remove, _} = T, #listener{socket = Sock}) -> gen_tcp:close(Sock), - x(T). + x(T); + +%% Possibly death of an accepting process monitored in old code. +l(_, S) -> + S. %% t/2 %% diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 6f3a4801ee..4c82d4dee2 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -248,17 +248,14 @@ all() -> groups() -> Ts = tc(), Sctp = ?util:have_sctp(), - [{?util:name([R,D,A,C]), [parallel], Ts} || R <- ?ENCODINGS, - D <- ?RFCS, - A <- ?ENCODINGS, - C <- ?CONTAINERS] + [{B, [P], Ts} || {B,P} <- [{true, shuffle}, {false, parallel}]] ++ [{?util:name([T,R,D,A,C,SD,CD]), [], [start_services, add_transports, result_codes, - {group, ?util:name([R,D,A,C])}, + {group, SD orelse CD}, remove_transports, stop_services]} || T <- ?TRANSPORTS, diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk index cb750c69a3..23219950bb 100644 --- a/lib/diameter/vsn.mk +++ b/lib/diameter/vsn.mk @@ -17,5 +17,5 @@ # %CopyrightEnd% APPLICATION = diameter -DIAMETER_VSN = 1.12 +DIAMETER_VSN = 1.12.1 APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN) diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 8ae495bb1b..3c1a6f2afd 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -647,22 +647,22 @@ init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). init_per_group_ipv6(GroupName, Config, Init) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), - Init( - snmp_test_lib:init_group_top_dir( - GroupName, - [{ipfamily, inet6}, - {ip, ?LOCALHOST(inet6)} - | lists:keydelete(ip, 1, Config)])); - {error, _} -> - {skip, "Host seems to not support IPv6"} - end; + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> + Init( + snmp_test_lib:init_group_top_dir( + GroupName, + [{ipfamily, inet6}, + {ip, ?LOCALHOST(inet6)} + | lists:keydelete(ip, 1, Config)])); + false -> + {skip, "Host does not support IPV6"} + end; _ -> - {skip, "Host does not support IPV6"} + {skip, "Test config ipv6_hosts is missing"} end. end_per_group(all_tcs, Config) -> diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index d17882e765..71f4017d8b 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -583,38 +583,38 @@ init_per_group(event_tests_mt = GroupName, Config) -> GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(ipv6_mt = GroupName, Config) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> ipv6_init( snmp_test_lib:init_group_top_dir( GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config])); - {error, _} -> - {skip, "Host seems to not support IPv6"} + false -> + {skip, "Host does not support IPv6"} end; _ -> - {skip, "Host does not support IPV6"} + {skip, "Test config ipv6_hosts is missing"} end; init_per_group(ipv6 = GroupName, Config) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); - {error, _} -> - {skip, "Host seems to not support IPv6"} + false -> + {skip, "Host does not support IPv6"} end; _ -> - {skip, "Host does not support IPV6"} + {skip, "Test config ipv6_hosts is missing"} end; init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). - + end_per_group(_GroupName, Config) -> %% Do we really need to do this? lists:keydelete(snmp_group_top_dir, 1, Config). diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl index ac9e37bc8b..24c14d86ea 100644 --- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl +++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl @@ -121,14 +121,14 @@ init_per_group(_, Config) -> Config. init_per_group_ipv6(Families, Config) -> + {ok, Hostname0} = inet:gethostname(), case ct:require(ipv6_hosts) of ok -> - case gen_udp:open(0, [inet6]) of - {ok, S} -> - ok = gen_udp:close(S), + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> init_per_group_ip(Families, Config); - {error, _} -> - {skip, "Host seems to not support IPv6"} + false -> + {skip, "Host does not support IPv6"} end; _ -> {skip, "Test config ipv6_hosts is missing"} diff --git a/lib/ssl/doc/src/ssl_app.xml b/lib/ssl/doc/src/ssl_app.xml index a66e947bc1..f317dfded4 100644 --- a/lib/ssl/doc/src/ssl_app.xml +++ b/lib/ssl/doc/src/ssl_app.xml @@ -141,6 +141,16 @@ marker="ssl#clear_pem_cache-0">ssl:clear_pem_cache/0</seealso> </item> + + <tag><c><![CDATA[bypass_pem_cache = boolean() <optional>]]></c></tag> + <item> + <p>Introduced in ssl-8.0.2. Disables the PEM-cache. + The PEM cache has proven to be a bottleneck, until the + implementation has been improved this can be used as + a workaround. Defaults to false. + </p> + </item> + <tag><c><![CDATA[alert_timeout = integer() <optional>]]></c></tag> <item> <p> diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 3ec3f50e05..f359655d85 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -64,7 +64,7 @@ trusted_cert_and_path(CertChain, CertDbHandle, CertDbRef, PartialChainHandler) - {ok, IssuerId} = public_key:pkix_issuer_id(OtpCert, self), {self, IssuerId}; false -> - other_issuer(OtpCert, BinCert, CertDbHandle) + other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) end, case SignedAndIssuerID of @@ -200,7 +200,7 @@ certificate_chain(OtpCert, BinCert, CertDbHandle, CertsDbRef, Chain) -> {_, true = SelfSigned} -> certificate_chain(CertDbHandle, CertsDbRef, Chain, ignore, ignore, SelfSigned); {{error, issuer_not_found}, SelfSigned} -> - case find_issuer(OtpCert, BinCert, CertDbHandle) of + case find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) of {ok, {SerialNr, Issuer}} -> certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, SelfSigned); @@ -232,7 +232,7 @@ certificate_chain(CertDbHandle, CertsDbRef, Chain, SerialNr, Issuer, _SelfSigned {ok, undefined, lists:reverse(Chain)} end. -find_issuer(OtpCert, BinCert, CertDbHandle) -> +find_issuer(OtpCert, BinCert, CertDbHandle, CertsDbRef) -> IsIssuerFun = fun({_Key, {_Der, #'OTPCertificate'{} = ErlCertCandidate}}, Acc) -> case public_key:pkix_is_issuer(OtpCert, ErlCertCandidate) of @@ -250,12 +250,24 @@ find_issuer(OtpCert, BinCert, CertDbHandle) -> Acc end, - try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of - issuer_not_found -> - {error, issuer_not_found} - catch - {ok, _IssuerId} = Return -> - Return + if is_reference(CertsDbRef) -> % actual DB exists + try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, CertDbHandle) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _IssuerId} = Return -> + Return + end; + is_tuple(CertsDbRef), element(1,CertsDbRef) =:= extracted -> % cache bypass byproduct + {extracted, CertsData} = CertsDbRef, + DB = [Entry || {decoded, Entry} <- CertsData], + try lists:foldl(IsIssuerFun, issuer_not_found, DB) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _IssuerId} = Return -> + Return + end end. is_valid_extkey_usage(KeyUse, client) -> @@ -281,12 +293,12 @@ public_key(#'OTPSubjectPublicKeyInfo'{algorithm = #'PublicKeyAlgorithm'{algorith subjectPublicKey = Key}) -> {Key, Params}. -other_issuer(OtpCert, BinCert, CertDbHandle) -> +other_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) -> case public_key:pkix_issuer_id(OtpCert, other) of {ok, IssuerId} -> {other, IssuerId}; {error, issuer_not_found} -> - case find_issuer(OtpCert, BinCert, CertDbHandle) of + case find_issuer(OtpCert, BinCert, CertDbHandle, CertDbRef) of {ok, IssuerId} -> {other, IssuerId}; Other -> diff --git a/lib/ssl/src/ssl_crl.erl b/lib/ssl/src/ssl_crl.erl index d9f21e04ac..01be1fb9ab 100644 --- a/lib/ssl/src/ssl_crl.erl +++ b/lib/ssl/src/ssl_crl.erl @@ -47,7 +47,7 @@ trusted_cert_and_path(CRL, issuer_not_found, {Db, DbRef} = DbHandle) -> {ok, unknown_crl_ca, []} end. -find_issuer(CRL, {Db,_}) -> +find_issuer(CRL, {Db,DbRef}) -> Issuer = public_key:pkix_normalize_name(public_key:pkix_crl_issuer(CRL)), IsIssuerFun = fun({_Key, {_Der,ErlCertCandidate}}, Acc) -> @@ -55,15 +55,27 @@ find_issuer(CRL, {Db,_}) -> (_, Acc) -> Acc end, - - try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of - issuer_not_found -> - {error, issuer_not_found} - catch - {ok, _} = Result -> - Result + if is_reference(DbRef) -> % actual DB exists + try ssl_pkix_db:foldl(IsIssuerFun, issuer_not_found, Db) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _} = Result -> + Result + end; + is_tuple(DbRef), element(1,DbRef) =:= extracted -> % cache bypass byproduct + {extracted, CertsData} = DbRef, + Certs = [Entry || {decoded, Entry} <- CertsData], + try lists:foldl(IsIssuerFun, issuer_not_found, Certs) of + issuer_not_found -> + {error, issuer_not_found} + catch + {ok, _} = Result -> + Result + end end. + verify_crl_issuer(CRL, ErlCertCandidate, Issuer, NotIssuer) -> TBSCert = ErlCertCandidate#'OTPCertificate'.tbsCertificate, case public_key:pkix_normalize_name(TBSCert#'OTPTBSCertificate'.subject) of diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 36d533cd4e..5b51ac0916 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -1216,13 +1216,18 @@ certificate_authorities(CertDbHandle, CertDbRef) -> end, list_to_binary([Enc(Cert) || {_, Cert} <- Authorities]). -certificate_authorities_from_db(CertDbHandle, CertDbRef) -> +certificate_authorities_from_db(CertDbHandle, CertDbRef) when is_reference(CertDbRef) -> ConnectionCerts = fun({{Ref, _, _}, Cert}, Acc) when Ref == CertDbRef -> [Cert | Acc]; (_, Acc) -> Acc end, - ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle). + ssl_pkix_db:foldl(ConnectionCerts, [], CertDbHandle); +certificate_authorities_from_db(_CertDbHandle, {extracted, CertDbData}) -> + %% Cache disabled, Ref contains data + lists:foldl(fun({decoded, {_Key,Cert}}, Acc) -> [Cert | Acc] end, + [], CertDbData). + %%-------------Extension handling -------------------------------- diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index c7dcbaabe9..5bd9521de7 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -115,13 +115,25 @@ start_link_dist(Opts) -> %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- connection_init({der, _} = Trustedcerts, Role, CRLCache) -> - call({connection_init, Trustedcerts, Role, CRLCache}); + case bypass_pem_cache() of + true -> + {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts), + call({connection_init, Extracted, Role, CRLCache}); + false -> + call({connection_init, Trustedcerts, Role, CRLCache}) + end; connection_init(<<>> = Trustedcerts, Role, CRLCache) -> call({connection_init, Trustedcerts, Role, CRLCache}); connection_init(Trustedcerts, Role, CRLCache) -> - call({connection_init, Trustedcerts, Role, CRLCache}). + case bypass_pem_cache() of + true -> + {ok, Extracted} = ssl_pkix_db:extract_trusted_certs(Trustedcerts), + call({connection_init, Extracted, Role, CRLCache}); + false -> + call({connection_init, Trustedcerts, Role, CRLCache}) + end. %%-------------------------------------------------------------------- -spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}. @@ -129,13 +141,18 @@ connection_init(Trustedcerts, Role, CRLCache) -> %% Description: Cache a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of - [{Content,_}] -> - {ok, Content}; - [Content] -> - {ok, Content}; - undefined -> - call({cache_pem, File}) + case bypass_pem_cache() of + true -> + ssl_pkix_db:decode_pem_file(File); + false -> + case ssl_pkix_db:lookup_cached_pem(DbHandle, File) of + [{Content,_}] -> + {ok, Content}; + [Content] -> + {ok, Content}; + undefined -> + call({cache_pem, File}) + end end. %%-------------------------------------------------------------------- @@ -506,6 +523,14 @@ delay_time() -> ?CLEAN_SESSION_DB end. +bypass_pem_cache() -> + case application:get_env(ssl, bypass_pem_cache) of + {ok, Bool} when is_boolean(Bool) -> + Bool; + _ -> + false + end. + max_session_cache_size(CacheType) -> case application:get_env(ssl, CacheType) of {ok, Size} when is_integer(Size) -> diff --git a/lib/ssl/src/ssl_pkix_db.erl b/lib/ssl/src/ssl_pkix_db.erl index b16903d7c7..0006ce14d9 100644 --- a/lib/ssl/src/ssl_pkix_db.erl +++ b/lib/ssl/src/ssl_pkix_db.erl @@ -29,10 +29,11 @@ -include_lib("kernel/include/file.hrl"). -export([create/0, add_crls/3, remove_crls/2, remove/1, add_trusted_certs/3, + extract_trusted_certs/1, remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1, ref_count/3, lookup_trusted_cert/4, foldl/3, select_cert_by_issuer/2, lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3, - lookup/2]). + decode_pem_file/1, lookup/2]). %%==================================================================== %% Internal application API @@ -82,12 +83,22 @@ remove(Dbs) -> %% <SerialNumber, Issuer>. Ref is used as it is specified %% for each connection which certificates are trusted. %%-------------------------------------------------------------------- -lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> +lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) when is_reference(Ref) -> case lookup({Ref, SerialNumber, Issuer}, DbHandle) of undefined -> undefined; [Certs] -> {ok, Certs} + end; +lookup_trusted_cert(_DbHandle, {extracted,Certs}, SerialNumber, Issuer) -> + try + [throw(Cert) + || {decoded, {{_Ref,CertSerial,CertIssuer}, Cert}} <- Certs, + CertSerial =:= SerialNumber, CertIssuer =:= Issuer], + undefined + catch + Cert -> + {ok, Cert} end. lookup_cached_pem([_, _, PemChache | _], File) -> @@ -103,6 +114,9 @@ lookup_cached_pem(PemChache, File) -> %% runtime database. Returns Ref that should be handed to lookup_trusted_cert %% together with the cert serialnumber and issuer. %%-------------------------------------------------------------------- +add_trusted_certs(_Pid, {extracted, _} = Certs, _) -> + {ok, Certs}; + add_trusted_certs(_Pid, {der, DerList}, [CertDb, _,_ | _]) -> NewRef = make_ref(), add_certs_from_der(DerList, NewRef, CertDb), @@ -122,6 +136,21 @@ add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache | _] = Db) -> undefined -> new_trusted_cert_entry(File, Db) end. + +extract_trusted_certs({der, DerList}) -> + {ok, {extracted, certs_from_der(DerList)}}; +extract_trusted_certs(File) -> + case file:read_file(File) of + {ok, PemBin} -> + Content = public_key:pem_decode(PemBin), + DerList = [Cert || {'Certificate', Cert, not_encrypted} <- Content], + {ok, {extracted, certs_from_der(DerList)}}; + Error -> + %% Have to simulate a failure happening in a server for + %% external handlers. + {error, {badmatch, Error}} + end. + %%-------------------------------------------------------------------- %% %% Description: Cache file as binary in DB @@ -141,6 +170,18 @@ cache_pem_file(Ref, File, [_CertsDb, _RefDb, PemChache| _]) -> insert(File, {Content, Ref}, PemChache), {ok, Content}. +-spec decode_pem_file(binary()) -> {ok, term()}. +decode_pem_file(File) -> + case file:read_file(File) of + {ok, PemBin} -> + Content = public_key:pem_decode(PemBin), + {ok, Content}; + Error -> + %% Have to simulate a failure happening in a server for + %% external handlers. + {error, {badmatch, Error}} + end. + %%-------------------------------------------------------------------- -spec remove_trusted_certs(reference(), db_handle()) -> ok. %% @@ -203,6 +244,8 @@ select_cert_by_issuer(Cache, Issuer) -> %% %% Description: Updates a reference counter in a <Db>. %%-------------------------------------------------------------------- +ref_count({extracted, _}, _Db, _N) -> + not_cached; ref_count(Key, Db, N) -> ets:update_counter(Db,Key,N). @@ -248,23 +291,39 @@ add_certs_from_der(DerList, Ref, CertsDb) -> [Add(Cert) || Cert <- DerList], ok. +certs_from_der(DerList) -> + Ref = make_ref(), + [Decoded || Cert <- DerList, + Decoded <- [decode_certs(Ref, Cert)], + Decoded =/= undefined]. + add_certs_from_pem(PemEntries, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries], ok. add_certs(Cert, Ref, CertsDb) -> + try + {decoded, {Key, Val}} = decode_certs(Ref, Cert), + insert(Key, Val, CertsDb) + catch + error:_ -> + ok + end. + +decode_certs(Ref, Cert) -> try ErlCert = public_key:pkix_decode_cert(Cert, otp), TBSCertificate = ErlCert#'OTPCertificate'.tbsCertificate, SerialNumber = TBSCertificate#'OTPTBSCertificate'.serialNumber, Issuer = public_key:pkix_normalize_name( TBSCertificate#'OTPTBSCertificate'.issuer), - insert({Ref, SerialNumber, Issuer}, {Cert,ErlCert}, CertsDb) + {decoded, {{Ref, SerialNumber, Issuer}, {Cert, ErlCert}}} catch error:_ -> Report = io_lib:format("SSL WARNING: Ignoring a CA cert as " "it could not be correctly decoded.~n", []), - error_logger:info_report(Report) + error_logger:info_report(Report), + undefined end. new_trusted_cert_entry(File, [CertsDb, RefDb, _ | _] = Db) -> diff --git a/lib/ssl/test/ssl.spec b/lib/ssl/test/ssl.spec index 86e14c033e..0ad94e22bc 100644 --- a/lib/ssl/test/ssl.spec +++ b/lib/ssl/test/ssl.spec @@ -1,4 +1,5 @@ {suites,"../ssl_test",all}. {skip_cases, "../ssl_test", - ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple], + ssl_bench_SUITE, [setup_sequential, setup_concurrent, payload_simple, + use_pem_cache, bypass_pem_cache], "Benchmarks run separately"}. diff --git a/lib/ssl/test/ssl_ECC_SUITE.erl b/lib/ssl/test/ssl_ECC_SUITE.erl index 69ac9908fa..258922d128 100644 --- a/lib/ssl/test/ssl_ECC_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_SUITE.erl @@ -145,7 +145,7 @@ init_per_testcase(TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]), end_per_testcase(TestCase, Config), - ssl:start(), + ssl_test_lib:clean_start(), ct:timetrap({seconds, 15}), Config. diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl index da181faf64..9d57e89b9b 100644 --- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl @@ -71,7 +71,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 8ffee751fc..57963fd44b 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -250,7 +250,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), @@ -307,6 +307,7 @@ init_per_testcase(protocol_versions, Config) -> init_per_testcase(reuse_session_expired, Config) -> ssl:stop(), application:load(ssl), + ssl_test_lib:clean_env(), application:set_env(ssl, session_lifetime, ?EXPIRE), application:set_env(ssl, session_delay_cleanup_time, 500), ssl:start(), @@ -316,6 +317,7 @@ init_per_testcase(reuse_session_expired, Config) -> init_per_testcase(empty_protocol_versions, Config) -> ssl:stop(), application:load(ssl), + ssl_test_lib:clean_env(), application:set_env(ssl, protocol_version, []), ssl:start(), ct:timetrap({seconds, 5}), @@ -454,6 +456,11 @@ end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_delay_cleanup_time), end_per_testcase(default_action, Config); +end_per_testcase(Case, Config) when Case == protocol_versions; + Case == empty_protocol_versions-> + application:unset_env(ssl, protocol_versions), + end_per_testcase(default_action, Config); + end_per_testcase(_TestCase, Config) -> Config. diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl index ed439a425f..21989f8d99 100644 --- a/lib/ssl/test/ssl_bench_SUITE.erl +++ b/lib/ssl/test/ssl_bench_SUITE.erl @@ -25,11 +25,12 @@ suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. -all() -> [{group, setup}, {group, payload}]. +all() -> [{group, setup}, {group, payload}, {group, pem_cache}]. groups() -> [{setup, [{repeat, 3}], [setup_sequential, setup_concurrent]}, - {payload, [{repeat, 3}], [payload_simple]} + {payload, [{repeat, 3}], [payload_simple]}, + {pem_cache, [{repeat, 3}], [use_pem_cache, bypass_pem_cache]} ]. init_per_group(_GroupName, Config) -> @@ -49,9 +50,33 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ok. +init_per_testcase(use_pem_cache, Conf) -> + case bypass_pem_cache_supported() of + false -> {skipped, "PEM cache bypass support required"}; + true -> + application:set_env(ssl, bypass_pem_cache, false), + Conf + end; +init_per_testcase(bypass_pem_cache, Conf) -> + case bypass_pem_cache_supported() of + false -> {skipped, "PEM cache bypass support required"}; + true -> + application:set_env(ssl, bypass_pem_cache, true), + Conf + end; init_per_testcase(_Func, Conf) -> Conf. +end_per_testcase(use_pem_cache, _Config) -> + case bypass_pem_cache_supported() of + false -> ok; + true -> application:set_env(ssl, bypass_pem_cache, false) + end; +end_per_testcase(bypass_pem_cache, _Config) -> + case bypass_pem_cache_supported() of + false -> ok; + true -> application:set_env(ssl, bypass_pem_cache, false) + end; end_per_testcase(_Func, _Conf) -> ok. @@ -94,6 +119,18 @@ payload_simple(Config) -> {suite, "ssl"}, {name, "Payload simple"}]}), ok. +use_pem_cache(_Config) -> + {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()), + ct_event:notify(#event{name = benchmark_data, + data=[{value, Result}, + {suite, "ssl"}, {name, "Use PEM cache"}]}). + +bypass_pem_cache(_Config) -> + {ok, Result} = do_test(ssl, pem_cache, 100, 500, node()), + ct_event:notify(#event{name = benchmark_data, + data=[{value, Result}, + {suite, "ssl"}, {name, "Bypass PEM cache"}]}). + ssl() -> test(ssl, ?COUNT, node()). @@ -172,6 +209,18 @@ server_init(ssl, payload, Loop, _, Server) -> ssl:close(TSocket) end, setup_server_connection(Socket, Test); +server_init(ssl, pem_cache, Loop, _, Server) -> + {ok, Socket} = ssl:listen(0, ssl_opts(listen_der)), + {ok, {_Host, Port}} = ssl:sockname(Socket), + {ok, Host} = inet:gethostname(), + Server ! {self(), {init, Host, Port}}, + Test = fun(TSocket) -> + ok = ssl:ssl_accept(TSocket), + Size = byte_size(msg()), + server_echo(TSocket, Size, Loop), + ssl:close(TSocket) + end, + setup_server_connection(Socket, Test); server_init(Type, Tc, _, _, Server) -> io:format("No server init code for ~p ~p~n",[Type, Tc]), @@ -185,6 +234,11 @@ client_init(Master, ssl, payload, Host, Port) -> Master ! {self(), init}, Size = byte_size(msg()), {Sock, Size}; +client_init(Master, ssl, pem_cache, Host, Port) -> + {ok, Sock} = ssl:connect(Host, Port, ssl_opts(connect_der)), + Master ! {self(), init}, + Size = byte_size(msg()), + {Sock, Size}; client_init(_Me, Type, Tc, Host, Port) -> io:format("No client init code for ~p ~p~n",[Type, Tc]), {Host, Port}. @@ -228,6 +282,13 @@ payload(Loop, ssl, D = {Socket, Size}) when Loop > 0 -> payload(_, _, {Socket, _}) -> ssl:close(Socket). +pem_cache(N, ssl, Data = {Socket, Size}) when N > 0 -> + ok = ssl:send(Socket, msg()), + {ok, _} = ssl:recv(Socket, Size), + pem_cache(N-1, ssl, Data); +pem_cache(_, _, {Socket, _}) -> + ssl:close(Socket). + msg() -> <<"Hello", 0:(512*8), @@ -352,16 +413,43 @@ stop_profile(fprof, File) -> ssl_opts(listen) -> [{backlog, 500} | ssl_opts("server")]; ssl_opts(connect) -> - [{verify, verify_peer} - | ssl_opts("client")]; + [{verify, verify_peer} | ssl_opts("client")]; +ssl_opts(listen_der) -> + [{backlog, 500} | ssl_opts("server_der")]; +ssl_opts(connect_der) -> + [{verify, verify_peer} | ssl_opts("client_der")]; ssl_opts(Role) -> - Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + CertData = cert_data(Role), [{active, false}, {depth, 2}, {reuseaddr, true}, {mode,binary}, {nodelay, true}, - {ciphers, [{dhe_rsa,aes_256_cbc,sha}]}, - {cacertfile, filename:join([Dir, Role, "cacerts.pem"])}, + {ciphers, [{dhe_rsa,aes_256_cbc,sha}]} + |CertData]. + +cert_data(Der) when Der =:= "server_der"; Der =:= "client_der" -> + [Role,_] = string:tokens(Der, "_"), + Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + {ok, CaCert0} = file:read_file(filename:join([Dir, Role, "cacerts.pem"])), + {ok, Cert0} = file:read_file(filename:join([Dir, Role, "cert.pem"])), + {ok, Key0} = file:read_file(filename:join([Dir, Role, "key.pem"])), + [{_, Cert, _}] = public_key:pem_decode(Cert0), + CaCert1 = public_key:pem_decode(CaCert0), + CaCert = [CCert || {_, CCert, _} <- CaCert1], + [{KeyType, Key, _}] = public_key:pem_decode(Key0), + [{cert, Cert}, + {cacerts, CaCert}, + {key, {KeyType, Key}}]; +cert_data(Role) -> + Dir = filename:join([code:lib_dir(ssl), "examples", "certs", "etc"]), + [{cacertfile, filename:join([Dir, Role, "cacerts.pem"])}, {certfile, filename:join([Dir, Role, "cert.pem"])}, {keyfile, filename:join([Dir, Role, "key.pem"])}]. + +bypass_pem_cache_supported() -> + %% This function is currently critical to support cache bypass + %% and did not exist in prior versions. + catch ssl_pkix_db:module_info(), % ensure module is loaded + erlang:function_exported(ssl_pkix_db, extract_trusted_certs, 1). + diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index c83c513eb3..4c6f1d7c01 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -85,7 +85,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl index e37e127440..bc2822f0c4 100644 --- a/lib/ssl/test/ssl_crl_SUITE.erl +++ b/lib/ssl/test/ssl_crl_SUITE.erl @@ -136,7 +136,7 @@ init_per_testcase(Case, Config0) -> true -> end_per_testcase(Case, Config0), inets:start(), - ssl:start(), + ssl_test_lib:clean_start(), ServerRoot = make_dir_path([proplists:get_value(priv_dir, Config0), idp_crl, tmp]), %% start a HTTP server to serve the CRLs {ok, Httpd} = inets:start(httpd, [{ipfamily, proplists:get_value(ipfamily, Config0)}, @@ -155,7 +155,7 @@ init_per_testcase(Case, Config0) -> [{cert_dir, CertDir} | Config]; false -> end_per_testcase(Case, Config0), - ssl:start(), + ssl_test_lib:clean_start(), Config0 end. diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index a671e3e307..51f0651568 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -60,7 +60,7 @@ init_per_testcase(ignore_hassign_extension_pre_tls_1_2, Config0) -> ok -> case is_supported(sha512) of true -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index c55fa73cfb..a02881f1ae 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -68,7 +68,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 17237118a0..900b6de9ca 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -140,8 +140,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:stop(), - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index c0b762760d..cb1957327a 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -70,7 +70,7 @@ init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), proplists:get_value(priv_dir, Config)), ssl_test_lib:cert_options(Config) catch _:_ -> diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl index 13b0ce8ed9..02c98fc40f 100644 --- a/lib/ssl/test/ssl_pem_cache_SUITE.erl +++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl @@ -43,7 +43,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using oppenssl {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), @@ -63,14 +63,15 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(pem_cleanup = Case, Config) -> - end_per_testcase(Case, Config) , application:load(ssl), + end_per_testcase(Case, Config) , application:set_env(ssl, ssl_pem_cache_clean, ?CLEANUP_INTERVAL), ssl:start(), ct:timetrap({minutes, 1}), Config. end_per_testcase(_TestCase, Config) -> + ssl_test_lib:clean_env(), ssl:stop(), Config. diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index b352844ba0..28637fc32d 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -58,7 +58,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), %% make rsa certs using {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl index 34ef2e6af9..4e916a7f03 100644 --- a/lib/ssl/test/ssl_sni_SUITE.erl +++ b/lib/ssl/test/ssl_sni_SUITE.erl @@ -41,7 +41,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), ssl_test_lib:cert_options(Config0) diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index a92b978ca9..81f16030f7 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -1355,3 +1355,19 @@ ct_log_supported_protocol_versions(Config) -> _ -> ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]) end. + +clean_env() -> + application:unset_env(ssl, protocol_version), + application:unset_env(ssl, session_lifetime), + application:unset_env(ssl, session_cb), + application:unset_env(ssl, session_cb_init_args), + application:unset_env(ssl, session_cache_client_max), + application:unset_env(ssl, session_cache_server_max), + application:unset_env(ssl, ssl_pem_cache_clean), + application:unset_env(ssl, alert_timeout). + +clean_start() -> + ssl:stop(), + application:load(ssl), + clean_env(), + ssl:start(). diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 9ae032503a..9ecfe5b0ea 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -119,12 +119,7 @@ init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> - ssl:stop(), - application:load(ssl), - ct:pal("Before clean: Version: ~p", [ssl:versions()]), - application:unset_env(ssl, protocol_version), - ct:pal("After clean: Version: ~p", [ssl:versions()]), - ssl:start(), + ssl_test_lib:clean_start(), {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), proplists:get_value(priv_dir, Config0)), Config1 = ssl_test_lib:make_dsa_cert(Config0), diff --git a/lib/xmerl/src/xmerl_eventp.erl b/lib/xmerl/src/xmerl_eventp.erl index 2cb76abc6e..8d7ea25e24 100644 --- a/lib/xmerl/src/xmerl_eventp.erl +++ b/lib/xmerl/src/xmerl_eventp.erl @@ -25,6 +25,90 @@ %% Each contain more elaborate settings of xmerl_scan that makes usage of %% the customization functions. %% +%% @type xmlElement() = #xmlElement{}. +%% +%% @type option_list(). <p>Options allow to customize the behaviour of the +%% scanner. +%% See also <a href="xmerl_examples.html">tutorial</a> on customization +%% functions. +%% </p> +%% <p> +%% Possible options are: +%% </p> +%% <dl> +%% <dt><code>{acc_fun, Fun}</code></dt> +%% <dd>Call back function to accumulate contents of entity.</dd> +%% <dt><code>{continuation_fun, Fun} | +%% {continuation_fun, Fun, ContinuationState}</code></dt> +%% <dd>Call back function to decide what to do if the scanner runs into EOF +%% before the document is complete.</dd> +%% <dt><code>{event_fun, Fun} | +%% {event_fun, Fun, EventState}</code></dt> +%% <dd>Call back function to handle scanner events.</dd> +%% <dt><code>{fetch_fun, Fun} | +%% {fetch_fun, Fun, FetchState}</code></dt> +%% <dd>Call back function to fetch an external resource.</dd> +%% <dt><code>{hook_fun, Fun} | +%% {hook_fun, Fun, HookState}</code></dt> +%% <dd>Call back function to process the document entities once +%% identified.</dd> +%% <dt><code>{close_fun, Fun}</code></dt> +%% <dd>Called when document has been completely parsed.</dd> +%% <dt><code>{rules, ReadFun, WriteFun, RulesState} | +%% {rules, Rules}</code></dt> +%% <dd>Handles storing of scanner information when parsing.</dd> +%% <dt><code>{user_state, UserState}</code></dt> +%% <dd>Global state variable accessible from all customization functions</dd> +%% +%% <dt><code>{fetch_path, PathList}</code></dt> +%% <dd>PathList is a list of +%% directories to search when fetching files. If the file in question +%% is not in the fetch_path, the URI will be used as a file +%% name.</dd> +%% <dt><code>{space, Flag}</code></dt> +%% <dd>'preserve' (default) to preserve spaces, 'normalize' to +%% accumulate consecutive whitespace and replace it with one space.</dd> +%% <dt><code>{line, Line}</code></dt> +%% <dd>To specify starting line for scanning in document which contains +%% fragments of XML.</dd> +%% <dt><code>{namespace_conformant, Flag}</code></dt> +%% <dd>Controls whether to behave as a namespace conformant XML parser, +%% 'false' (default) to not otherwise 'true'.</dd> +%% <dt><code>{validation, Flag}</code></dt> +%% <dd>Controls whether to process as a validating XML parser: +%% 'off' (default) no validation, or validation 'dtd' by DTD or 'schema' +%% by XML Schema. 'false' and 'true' options are obsolete +%% (i.e. they may be removed in a future release), if used 'false' +%% equals 'off' and 'true' equals 'dtd'.</dd> +%% <dt><code>{schemaLocation, [{Namespace,Link}|...]}</code></dt> +%% <dd>Tells explicitly which XML Schema documents to use to validate +%% the XML document. Used together with the +%% <code>{validation,schema}</code> option.</dd> +%% <dt><code>{quiet, Flag}</code></dt> +%% <dd>Set to 'true' if xmerl should behave quietly and not output any +%% information to standard output (default 'false').</dd> +%% <dt><code>{doctype_DTD, DTD}</code></dt> +%% <dd>Allows to specify DTD name when it isn't available in the XML +%% document. This option has effect only together with +%% <code>{validation,'dtd'</code> option.</dd> +%% <dt><code>{xmlbase, Dir}</code></dt> +%% <dd>XML Base directory. If using string/1 default is current directory. +%% If using file/1 default is directory of given file.</dd> +%% <dt><code>{encoding, Enc}</code></dt> +%% <dd>Set default character set used (default UTF-8). +%% This character set is used only if not explicitly given by the XML +%% declaration. </dd> +%% <dt><code>{document, Flag}</code></dt> +%% <dd>Set to 'true' if xmerl should return a complete XML document +%% as an xmlDocument record (default 'false').</dd> +%% <dt><code>{comments, Flag}</code></dt> +%% <dd>Set to 'false' if xmerl should skip comments otherwise they will +%% be returned as xmlComment records (default 'true').</dd> +%% <dt><code>{default_attrs, Flag}</code></dt> +%% <dd>Set to 'true' if xmerl should add to elements missing attributes +%% with a defined default value (default 'false').</dd> +%% </dl> +%% -module(xmerl_eventp). -vsn('0.19'). -date('03-09-17'). diff --git a/lib/xmerl/src/xmerl_scan.erl b/lib/xmerl/src/xmerl_scan.erl index 2147a46a13..5e0459ec21 100644 --- a/lib/xmerl/src/xmerl_scan.erl +++ b/lib/xmerl/src/xmerl_scan.erl @@ -111,13 +111,16 @@ %% <dd>Set to 'true' if xmerl should add to elements missing attributes %% with a defined default value (default 'false').</dd> %% </dl> +%% @type xmlElement() = #xmlElement{}. +%% The record definition is found in xmerl.hrl. +%% @type xmlDocument() = #xmlDocument{}. +%% The record definition is found in xmerl.hrl. %% @type document() = xmlElement() | xmlDocument(). <p> %% The document returned by <tt>xmerl_scan:string/[1,2]</tt> and %% <tt>xmerl_scan:file/[1,2]</tt>. The type of the returned record depends on %% the value of the document option passed to the function. %% </p> - -module(xmerl_scan). -vsn('0.20'). -date('03-09-16'). diff --git a/lib/xmerl/src/xmerl_xpath.erl b/lib/xmerl/src/xmerl_xpath.erl index bbebda1030..6146feba49 100644 --- a/lib/xmerl/src/xmerl_xpath.erl +++ b/lib/xmerl/src/xmerl_xpath.erl @@ -43,13 +43,27 @@ %% </pre> %% %% @type nodeEntity() = -%% xmlElement() -%% | xmlAttribute() -%% | xmlText() -%% | xmlPI() -%% | xmlComment() -%% | xmlNsNode() -%% | xmlDocument() +%% #xmlElement{} +%% | #xmlAttribute{} +%% | #xmlText{} +%% | #xmlPI{} +%% | #xmlComment{} +%% | #xmlNsNode{} +%% | #xmlDocument{} +%% +%% @type docNodes() = #xmlElement{} +%% | #xmlAttribute{} +%% | #xmlText{} +%% | #xmlPI{} +%% | #xmlComment{} +%% | #xmlNsNode{} +%% +%% @type docEntity() = #xmlDocument{} | [docNodes()] +%% +%% @type xPathString() = string() +%% +%% @type parentList() = [{atom(), integer()}] +%% %% @type option_list(). <p>Options allows to customize the behaviour of the %% XPath scanner. %% </p> @@ -115,7 +129,7 @@ string(Str, Doc, Options) -> %% Parents = parentList() %% Doc = nodeEntity() %% Options = option_list() -%% Scalar = xmlObj +%% Scalar = #xmlObj{} %% @doc Extracts the nodes from the parsed XML tree according to XPath. %% xmlObj is a record with fields type and value, %% where type is boolean | number | string diff --git a/lib/xmerl/src/xmerl_xs.erl b/lib/xmerl/src/xmerl_xs.erl index 3e9f6622b8..1ce76cfa41 100644 --- a/lib/xmerl/src/xmerl_xs.erl +++ b/lib/xmerl/src/xmerl_xs.erl @@ -45,7 +45,6 @@ % XSLT package which is written i C++. % See also the <a href="xmerl_xs_examples.html">Tutorial</a>. % </p> - -module(xmerl_xs). -export([xslapply/2, value_of/1, select/2, built_in_rules/2 ]). @@ -71,15 +70,13 @@ %% xslapply(fun template/1, E), %% "</h1>"]; %% </pre> - xslapply(Fun, EList) when is_list(EList) -> - lists:map( Fun, EList); + lists:map(Fun, EList); xslapply(Fun, E = #xmlElement{})-> lists:map( Fun, E#xmlElement.content). - %% @spec value_of(E) -> List -%% E = unknown() +%% E = term() %% %% @doc Concatenates all text nodes within the tree. %% diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl index 4b5efae8dd..a89b3159ec 100644 --- a/lib/xmerl/src/xmerl_xsd.erl +++ b/lib/xmerl/src/xmerl_xsd.erl @@ -49,6 +49,7 @@ %% <dd>It is possible by this option to provide a state with process %% information from an earlier validation.</dd> %% </dl> +%% @type filename() = string() %% @end %%%------------------------------------------------------------------- -module(xmerl_xsd). @@ -138,7 +139,7 @@ state2file(S=#xsd_state{schema_name=SN}) -> %% @spec state2file(State,FileName) -> ok | {error,Reason} %% State = global_state() -%% FileName = filename() +%% FileName = string() %% @doc Saves the schema state with all information of the processed %% schema in a file. You can provide the file name for the saved %% state. FileName is saved with the <code>.xss</code> extension @@ -153,7 +154,7 @@ state2file(S,FileName) when is_record(S,xsd_state) -> %% @spec file2state(FileName) -> {ok,State} | {error,Reason} %% State = global_state() -%% FileName = filename() +%% FileName = string() %% @doc Reads the schema state with all information of the processed %% schema from a file created with <code>state2file/[1,2]</code>. The %% format of this file is internal. The state can then be used @@ -202,7 +203,7 @@ xmerl_xsd_vsn_check(S=#xsd_state{vsn=MD5_VSN}) -> process_validate(Schema,Xml) -> process_validate(Schema,Xml,[]). %% @spec process_validate(Schema,Element,Options) -> Result -%% Schema = filename() +%% Schema = string() %% Element = XmlElement %% Options = option_list() %% Result = {ValidXmlElement,State} | {error,Reason} @@ -282,7 +283,7 @@ validate3(_,_,S) -> process_schema(Schema) -> process_schema(Schema,[]). %% @spec process_schema(Schema,Options) -> Result -%% Schema = filename() +%% Schema = string() %% Result = {ok,State} | {error,Reason} %% State = global_state() %% Reason = [ErrorReason] | ErrorReason @@ -324,7 +325,7 @@ process_schema2({SE,_},State,_Schema) -> process_schemas(Schemas) -> process_schemas(Schemas,[]). %% @spec process_schemas(Schemas,Options) -> Result -%% Schemas = [{NameSpace,filename()}|Schemas] | [] +%% Schemas = [{NameSpace,string()}|Schemas] | [] %% Result = {ok,State} | {error,Reason} %% Reason = [ErrorReason] | ErrorReason %% Options = option_list() @@ -5426,7 +5427,7 @@ add_key_once(Key,N,El,L) -> %% {filename:join([[io_lib:format("/~w(~w)",[X,Y])||{X,Y}<-Parents],Type]),Pos}. %% @spec format_error(Errors) -> Result -%% Errors = error_tuple() | [error_tuple()] +%% Errors = tuple() | [tuple()] %% Result = string() | [string()] %% @doc Formats error descriptions to human readable strings. format_error(L) when is_list(L) -> diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index a78a035a1f..95adaa5bb0 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.3.11 +XMERL_VSN = 1.3.12 |