diff options
Diffstat (limited to 'lib/diameter')
46 files changed, 764 insertions, 401 deletions
diff --git a/lib/diameter/bin/diameterc b/lib/diameter/bin/diameterc index d31f341c36..2c9a8f555c 100755 --- a/lib/diameter/bin/diameterc +++ b/lib/diameter/bin/diameterc @@ -4,7 +4,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -74,7 +74,7 @@ compile(#argv{file = File, options = Opts, output = Out}) -> ok -> 0; {error, Reason} -> - error_msg(Reason, []), + error_msg(diameter_make:format_error(Reason), []), 1 catch error: Reason -> diff --git a/lib/diameter/doc/src/book.xml b/lib/diameter/doc/src/book.xml index 960296528b..7b606c84d0 100644 --- a/lib/diameter/doc/src/book.xml +++ b/lib/diameter/doc/src/book.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> -<year>2011</year> +<year>2011</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 9864b21bc5..7d6a28e51c 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY spawn_opt '<seealso marker="erts:erlang#spawn_opt-2">erlang:spawn_opt/2</seealso>'> @@ -20,7 +20,8 @@ <header> <copyright> -<year>2011</year><year>2013</year> +<year>2011</year> +<year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -355,8 +356,8 @@ question communicates an address list as described in <tag><c>{'Origin-State-Id', &dict_Unsigned32;}</c></tag> <item> <p> -Origin-State-Id is optional but will be included in outgoing messages -sent by diameter itself: CER/CEA, DWR/DWA and DPR/DPA. +Origin-State-Id is optional but, if configured, will be included in +outgoing CER/CEA and DWR/DWA messages. Setting a value of <c>0</c> (zero) is equivalent to not setting a value, as documented in &the_rfc;. The function &origin_state_id; diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml index 0b6839dcb2..67c430c40a 100644 --- a/lib/diameter/doc/src/diameter_app.xml +++ b/lib/diameter/doc/src/diameter_app.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY message '<seealso marker="#message">message()</seealso>'> <!ENTITY dict diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml index 9d26466b25..308a56fab7 100644 --- a/lib/diameter/doc/src/diameter_codec.xml +++ b/lib/diameter/doc/src/diameter_codec.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY records '<seealso marker="diameter_dict#MESSAGE_RECORDS">diameter_dict(4)</seealso>'> diff --git a/lib/diameter/doc/src/diameter_dict.xml b/lib/diameter/doc/src/diameter_dict.xml index 4f51a12ebc..810a146b88 100644 --- a/lib/diameter/doc/src/diameter_dict.xml +++ b/lib/diameter/doc/src/diameter_dict.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "fileref.dtd" [ <!ENTITY format '<seealso marker="#FILE_FORMAT">FILE FORMAT</seealso>'> diff --git a/lib/diameter/doc/src/diameter_examples.xml b/lib/diameter/doc/src/diameter_examples.xml index 1fd7755695..7808d64b8d 100644 --- a/lib/diameter/doc/src/diameter_examples.xml +++ b/lib/diameter/doc/src/diameter_examples.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> -<year>2011</year><year>2012</year> +<year>2011</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> diff --git a/lib/diameter/doc/src/diameter_intro.xml b/lib/diameter/doc/src/diameter_intro.xml index 6c1d1910d2..93293f2d8e 100644 --- a/lib/diameter/doc/src/diameter_intro.xml +++ b/lib/diameter/doc/src/diameter_intro.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd" [ <!ENTITY % also SYSTEM "seealso.ent"> %also; diff --git a/lib/diameter/doc/src/diameter_make.xml b/lib/diameter/doc/src/diameter_make.xml index 1c1eff6c6a..0c7e6b794d 100644 --- a/lib/diameter/doc/src/diameter_make.xml +++ b/lib/diameter/doc/src/diameter_make.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY compile_forms2 '<seealso marker="compiler:compile#forms-2">compile:forms/2</seealso>'> @@ -16,7 +16,7 @@ <header> <copyright> <year>2012</year> -<year>2013</year> +<year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -52,7 +52,7 @@ under the License. <p> The function &codec; is used to compile a diameter &dictionary; into Erlang source. -The resulting source implements the interface diameter required +The resulting source implements the interface diameter requires to encode and decode the dictionary's messages and AVPs.</p> <p> @@ -175,6 +175,10 @@ Note that a dictionary's <c>&dict_name;</c>, together with the The <c>&dict_name;</c> of a literal input dictionary defaults to <c>dictionary</c>.</p> +<p> +A returned error reason can be converted into a readable string using +&format_error;.</p> + </desc> </func> @@ -206,6 +210,18 @@ The return value is also a parsed dictionary.</p> </desc> </func> +<!-- ===================================================================== --> + +<func> +<name>format_error(Reason) -> string()</name> +<fsummary>Turn an error reason into a readable string.</fsummary> +<desc> + +<p> +Turn an error reason returned by &codec; into a readable string.</p> +</desc> +</func> + </funcs> <!-- ===================================================================== --> diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml index 2be77e3dfd..6302cb1435 100644 --- a/lib/diameter/doc/src/diameter_sctp.xml +++ b/lib/diameter/doc/src/diameter_sctp.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY gen_sctp '<seealso marker="kernel:gen_sctp">gen_sctp(3)</seealso>'> <!ENTITY gen_sctp_open1 @@ -15,7 +15,8 @@ <erlref> <header> <copyright> -<year>2011</year><year>2013</year> +<year>2011</year> +<year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -113,7 +114,7 @@ and port respectively.</p> <p> Multiple <c>ip</c> options can be specified for a multihomed peer. If none are specified then the values of <c>Host-IP-Address</c> -in the <c>#diameter_service{}</c> record are used. +in the <c>diameter_service</c> record are used. (In particular, one of these must be specified.) Option <c>port</c> defaults to 3868 for a listening transport and 0 for a connecting transport.</p> @@ -131,25 +132,18 @@ the buffer size.</p> </warning> <p> -diameter_sctp uses the <c>transport_data</c> field of -the <c>#diameter_packet{}</c> record to communicate the stream on which an -inbound message has been received, or on which an outbound message -should be sent: the value will be of the form <c>{stream, Id}</c> -on an inbound message passed to a &app_handle_request; or -&app_handle_answer; callback. -For an outbound message, either <c>undefined</c> (explicitly or -by receiving the outbound message as a <c>binary()</c>) or a tuple -should be set in the return value of &app_handle_request; -(typically by retaining the value passed into this function) -or &app_prepare_request;. -The value <c>undefined</c> uses a "next outbound stream" id and -increments this modulo the total number outbound streams. -That is, successive values of <c>undefined</c> cycle through all -outbound streams.</p> - -<!-- TODO: Some way of getting at the number of available outbound --> -<!-- streams. --> - +The <c>transport_data</c> field of record <c>diameter_packet</c> +is used to communicate the stream on which an inbound message +has been received, or on which an outbound message should be sent. +The value will be of the form <c>{stream, Id}</c> for an inbound +message passed to a &app_handle_request; or &app_handle_answer; +callback. +For an outbound message, <c>{outstream, Id}</c> in the return value of +&app_handle_request; or &app_prepare_retransmit; sets the outbound +stream, the stream id being interpreted modulo the number of outbound +streams. +Any other value, or not setting a value, causes successive such sends +to cycle though all outbound streams.</p> </desc> </func> diff --git a/lib/diameter/doc/src/diameter_soc.xml b/lib/diameter/doc/src/diameter_soc.xml index 4f5419122f..d9159f84b5 100644 --- a/lib/diameter/doc/src/diameter_soc.xml +++ b/lib/diameter/doc/src/diameter_soc.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd" [ <!ENTITY % also SYSTEM "seealso.ent" > %also; diff --git a/lib/diameter/doc/src/diameter_soc_rfc6733.xml b/lib/diameter/doc/src/diameter_soc_rfc6733.xml index deb4d05b0f..34ec902632 100644 --- a/lib/diameter/doc/src/diameter_soc_rfc6733.xml +++ b/lib/diameter/doc/src/diameter_soc_rfc6733.xml @@ -1,9 +1,9 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!-- <copyright> -<year>2013</year> +<year>2013</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml index ce4d6cfd0f..f6bbe7dd23 100644 --- a/lib/diameter/doc/src/diameter_tcp.xml +++ b/lib/diameter/doc/src/diameter_tcp.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY start '<seealso marker="#start-3">start/3</seealso>'> <!ENTITY gen_tcp_connect3 diff --git a/lib/diameter/doc/src/diameter_transport.xml b/lib/diameter/doc/src/diameter_transport.xml index 9161bd1f48..1618d05c47 100644 --- a/lib/diameter/doc/src/diameter_transport.xml +++ b/lib/diameter/doc/src/diameter_transport.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd" [ <!ENTITY message '<seealso marker="#message">message()</seealso>'> <!ENTITY MESSAGES '<seealso marker="#MESSAGES">MESSAGES</seealso>'> diff --git a/lib/diameter/doc/src/diameter_using.xml b/lib/diameter/doc/src/diameter_using.xml index c487d94a16..4427d29c3c 100644 --- a/lib/diameter/doc/src/diameter_using.xml +++ b/lib/diameter/doc/src/diameter_using.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> diff --git a/lib/diameter/doc/src/diameterc.xml b/lib/diameter/doc/src/diameterc.xml index 039f4f9cdd..5bffe9a771 100644 --- a/lib/diameter/doc/src/diameterc.xml +++ b/lib/diameter/doc/src/diameterc.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="iso-8859-1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE comref SYSTEM "comref.dtd" [ <!ENTITY dictionary '<seealso marker="diameter_dict">dictionary file</seealso>'> diff --git a/lib/diameter/doc/src/notes.xml b/lib/diameter/doc/src/notes.xml index 18c712ec3d..675ffcfd18 100644 --- a/lib/diameter/doc/src/notes.xml +++ b/lib/diameter/doc/src/notes.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd" [ <!ENTITY % also SYSTEM "seealso.ent" > <!ENTITY % here SYSTEM "seehere.ent" > @@ -11,7 +11,7 @@ <header> <copyright> <year>2011</year> -<year>2013</year> +<year>2014</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -42,6 +42,164 @@ first.</p> <!-- ===================================================================== --> +<section><title>diameter 1.6</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Add missing check at dictionary compilation.</p> + <p> + In particular, that an AVP defined as having type Grouped + in an @avp_types section has a corresponding definition + in a @grouped section.</p> + <p> + Own Id: OTP-11561</p> + </item> + <item> + <p> + Correct documentation on the setting of Origin-State-Id</p> + <p> + It was incorrectly stated that the AVP would be set in an + outgoing DPR/DPA.</p> + <p> + Own Id: OTP-11583</p> + </item> + <item> + <p> + Change interface for communicating outbound stream id to + diameter_sctp</p> + <p> + The module uses the transport_data field of record + diameter_packet to communicate the stream on which the an + incoming message is received and on which an outgoing + message should be sent, the previous interface being that + both are communicated as a tuple of the form {stream, + Id}. However, since diameter retains the value of an + incoming request's transport_data unless the + corresponding answer message specifies otherwise, the + behaviour in this case is to send an answer on the + outbound stream with the same identifier as the that of + the inbound stream on which the request was received. If + the inbound stream id is greater than or equal to the + number of outbound streams then this is guaranteed to + fail, causing the transport process in question to + terminate. There is no relationship between inbound and + outbound stream identifiers so diameter_sctp's imposition + of one is simply wrong.</p> + <p> + Outbound stream ids are now communicated with a different + tuple: {outstream, Id}, interpreted modulo the number of + outbound streams. Thus, retention of an inbound request's + transport_data has no effect on the selection of an + outbound stream.</p> + <p> + The change in interface is not strictly backwards + compatible because of the new atom for the outbound + stream. However, as there is currently no documented way + of obtaining the available number of outbound streams for + a peer connection, there is no way for a client to have + known the range of ids from which it could reliably have + chosen with the previous interface, so any setting of the + outbound stream has probably been unintentional. Not + explicitly specifying an outbound stream now results in a + round-robin selection.</p> + <p> + Thanks to Sharmila Pillai for reporting the problem.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-11593</p> + </item> + <item> + <p> + Fix unicode path failure in diameter_make:codec/2.</p> + <p> + A dictionary path containing a unicode codepoint > 255 + caused the function to fail.</p> + <p> + Own Id: OTP-11655</p> + </item> + <item> + <p> + Fix 'accept' config to diameter_sctp.</p> + <p> + OTP-10893 added support for {accept, Match} tuples to + specify addresses or regexps that should be matched + against peer addresses to decide whether or not a newly + established association should be retained, but this + hasn't been functional in the SCTP case because of + missing support in inet(3).</p> + <p> + The display of both local and peer addresses in + diameter:service_info/2 output has also been corrected.</p> + <p> + Own Id: OTP-11661 Aux Id: OTP-10229 </p> + </item> + <item> + <p> + Be lenient with the M-bit in Grouped AVPs.</p> + <p> + RFC 6733 says this, in 4.4:</p> + <p> + <taglist><item><p><c>Receivers of a Grouped AVP that does + not have the 'M' (mandatory) bit set and one or more of + the encapsulated AVPs within the group has the 'M' + (mandatory) bit set MAY simply be ignored if the Grouped + AVP itself is unrecognized. The rule applies even if the + encapsulated AVP with its 'M' (mandatory) bit set is + further encapsulated within other sub-groups, i.e., other + Grouped AVPs embedded within the Grouped + AVP.</c></p></item></taglist></p> + <p> + The first sentence is mangled but take it to mean this:</p> + <p> + <taglist><item><p><c>An unrecognized AVP of type Grouped + that does not set the 'M' bit MAY be ignored even if one + of its encapsulated AVPs sets the 'M' + bit.</c></p></item></taglist></p> + <p> + This is a bit of a non-statement since if the AVP is + unrecognized then its type is unknown. We therefore don't + know that its data bytes contain encapsulated AVPs, so + can't but ignore any of those that set the M-bit. Doing + anything else when the type *is* known would be + inconsistent.</p> + <p> + OTP-11087 (R16B03) caused the M-bit on any unrecognized + AVP to be regarded as an error, unrecognized being taken + to mean "not explicitly defined as a member of its + container". (That is, an AVP that can't be packed into a + dedicated record field, which is slightly stronger than + "not defined".) This fixed the original intention for + top-level AVPs but broke the required leniency for + Grouped AVPs whose type is known. This leniency is now + restored.</p> + <p> + Note that dictionary files need to be recompiled for the + change to have effect.</p> + <p> + Thanks to Rory McKeown for reporting the problem.</p> + <p> + Own Id: OTP-11675 Aux Id: OTP-11087 </p> + </item> + <item> + <p> + Fix pick_peer case clause failure.</p> + <p> + In the case of {call_mutates_state, true} configuration + on the service in question, any peer selection that + failed to select a peer resulted in a case clause + failure. This was noticed in the case of a peer failover + in which an alternate peer wasn't available.</p> + <p> + Own Id: OTP-11789</p> + </item> + </list> + </section> + +</section> + <section><title>diameter 1.5</title> <section><title>Improvements and New Features</title> @@ -73,12 +231,6 @@ first.</p> <p> Own Id: OTP-11361</p> </item> - <item> - <p> - Fix silent make rules (Thanks to Anthony Ramine)</p> - <p> - Own Id: OTP-11514</p> - </item> </list> </section> diff --git a/lib/diameter/doc/src/ref_man.xml b/lib/diameter/doc/src/ref_man.xml index 1095887144..62ba02b0b5 100644 --- a/lib/diameter/doc/src/ref_man.xml +++ b/lib/diameter/doc/src/ref_man.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> diff --git a/lib/diameter/doc/src/seealso.ent b/lib/diameter/doc/src/seealso.ent index 7bf7460351..44541afb9b 100644 --- a/lib/diameter/doc/src/seealso.ent +++ b/lib/diameter/doc/src/seealso.ent @@ -4,7 +4,7 @@ %CopyrightBegin% -Copyright Ericsson AB 2012-2013. All Rights Reserved. +Copyright Ericsson AB 2012-2014. 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 @@ -117,6 +117,7 @@ significant. <!ENTITY make_codec '<seealso marker="diameter_make#codec-2">diameter_make:codec/2</seealso>'> <!ENTITY make_format '<seealso marker="diameter_make#format-1">diameter_make:format/1</seealso>'> <!ENTITY make_flatten '<seealso marker="diameter_make#flatten-1">diameter_make:flatten/1</seealso>'> +<!ENTITY make_format_error '<seealso marker="diameter_make#format_error-1">diameter_make:format_error/1</seealso>'> <!-- diameter_transport --> diff --git a/lib/diameter/doc/src/user_man.xml b/lib/diameter/doc/src/user_man.xml index a6416c7e23..f915fa5a66 100644 --- a/lib/diameter/doc/src/user_man.xml +++ b/lib/diameter/doc/src/user_man.xml @@ -1,11 +1,11 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="utf-8" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> -<year>2011</year> +<year>2011</year><year>2013</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/diameter/include/diameter.hrl b/lib/diameter/include/diameter.hrl index 79c4dce541..5a40e42300 100644 --- a/lib/diameter/include/diameter.hrl +++ b/lib/diameter/include/diameter.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -58,8 +58,8 @@ -record(diameter_header, {version, %% 8-bit unsigned length, %% 24-bit unsigned - cmd_code, %% 8-bit unsigned - application_id, %% 24-bit unsigned + cmd_code, %% 24-bit unsigned + application_id, %% 32-bit unsigned hop_by_hop_id, %% 32-bit unsigned end_to_end_id, %% 32-bit unsigned is_request, %% boolean() R flag diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl index 55aae3a243..c8f706dc3e 100644 --- a/lib/diameter/include/diameter_gen.hrl +++ b/lib/diameter/include/diameter_gen.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -25,6 +25,11 @@ -define(THROW(T), throw({?MODULE, T})). +%% Key to a value in the process dictionary that determines whether or +%% not an unrecognized AVP setting the M-bit should be regarded as an +%% error or not. See is_strict/0. +-define(STRICT_KEY, strict). + -type parent_name() :: atom(). %% parent = Message or AVP -type parent_record() :: tuple(). %% -type avp_name() :: atom(). @@ -35,6 +40,18 @@ -type grouped_avp() :: nonempty_improper_list(#diameter_avp{}, [avp()]). -type avp() :: non_grouped_avp() | grouped_avp(). +%% Use a (hopefully) unique key when manipulating the process +%% dictionary. + +putr(K,V) -> + put({?MODULE, K}, V). + +getr(K) -> + get({?MODULE, K}). + +eraser(K) -> + erase({?MODULE, K}). + %% --------------------------------------------------------------------------- %% # encode_avps/2 %% --------------------------------------------------------------------------- @@ -212,12 +229,61 @@ decode(Name, #diameter_avp{code = Code, vendor_id = Vid} = Avp, Acc) -> %% decode/4 +%% AVP is defined in the dictionary ... decode(Name, {AvpName, Type}, Avp, Acc) -> d(Name, Avp#diameter_avp{name = AvpName, type = Type}, Acc); +%% ... or not. decode(Name, 'AVP', Avp, Acc) -> decode_AVP(Name, Avp, Acc). +%% 6733, 4.4: +%% +%% Receivers of a Grouped AVP that does not have the 'M' (mandatory) +%% bit set and one or more of the encapsulated AVPs within the group +%% has the 'M' (mandatory) bit set MAY simply be ignored if the +%% Grouped AVP itself is unrecognized. The rule applies even if the +%% encapsulated AVP with its 'M' (mandatory) bit set is further +%% encapsulated within other sub-groups, i.e., other Grouped AVPs +%% embedded within the Grouped AVP. +%% +%% The first sentence is slightly mangled, but take it to mean this: +%% +%% An unrecognized AVP of type Grouped that does not set the 'M' bit +%% MAY be ignored even if one of its encapsulated AVPs sets the 'M' +%% bit. +%% +%% The text above is a change from RFC 3588, which instead says this: +%% +%% Further, if any of the AVPs encapsulated within a Grouped AVP has +%% the 'M' (mandatory) bit set, the Grouped AVP itself MUST also +%% include the 'M' bit set. +%% +%% Both of these texts have problems. If the AVP is unknown then its +%% type is unknown since the type isn't sent over the wire, so the +%% 6733 text becomes a non-statement: don't know that the AVP not +%% setting the M-bit is of type Grouped, therefore can't know that its +%% data consists of encapsulated AVPs, therefore can't but ignore that +%% one of these might set the M-bit. It should be no worse if we know +%% the AVP to have type Grouped. +%% +%% Similarly, for the 3588 text: if we receive an AVP that doesn't set +%% the M-bit and don't know that the AVP has type Grouped then we +%% can't realize that its data contains an AVP that sets the M-bit, so +%% can't regard the AVP as erroneous on this account. Again, it should +%% be no worse if the type is known to be Grouped, but in this case +%% the RFC forces us to regard the AVP as erroneous. This is +%% inconsistent, and the 3588 text has never been enforced. +%% +%% So, if an AVP doesn't set the M-bit then we're free to ignore it, +%% regardless of the AVP's type. If we know the type to be Grouped +%% then we must ignore the M-bit on an encapsulated AVP. That means +%% packing such an encapsulated AVP into an 'AVP' field if need be, +%% not regarding the lack of a specific field as an error as is +%% otherwise the case. (The lack of an AVP-specific field being how we +%% defined the RFC's "unrecognized", which is slightly stronger than +%% "not defined".) + %% d/3 %% Don't try to decode the value of a Failed-AVP component since it @@ -230,9 +296,19 @@ d('Failed-AVP' = Name, Avp, Acc) -> %% Or try to decode. d(Name, Avp, {Avps, Acc}) -> #diameter_avp{name = AvpName, - data = Data} + data = Data, + type = Type, + is_mandatory = M} = Avp, + %% Use the process dictionary is to keep track of whether or not + %% to ignore an M-bit on an encapsulated AVP. Not ideal, but the + %% alternative requires widespread changes to be able to pass the + %% value around through the entire decode. The solution here is + %% simple in comparison, both to implement and to understand. + + Reset = relax(Type, M), + try avp(decode, Data, AvpName) of V -> {H, A} = ungroup(V, Avp), @@ -250,8 +326,32 @@ d(Name, Avp, {Avps, Acc}) -> {Reason, Avp, erlang:get_stacktrace()}), {Rec, Failed} = Acc, {[Avp|Avps], {Rec, [rc(Reason, Avp) | Failed]}} + after + relax(Reset) end. +%% Set false in the process dictionary as soon as we see a Grouped AVP +%% that doesn't set the M-bit, so that is_strict() can say whether or +%% not to ignore the M-bit on an encapsulated AVP. +relax('Grouped', M) -> + V = getr(?STRICT_KEY), + if V == undefined andalso not M -> + putr(?STRICT_KEY, M); + true -> + false + end; +relax(_, _) -> + false. + +%% Reset strictness. +relax(undefined) -> + eraser(?STRICT_KEY); +relax(false) -> + ok. + +is_strict() -> + false /= getr(?STRICT_KEY). + %% decode_AVP/3 %% %% Don't know this AVP: see if it can be packed in an 'AVP' field @@ -310,15 +410,8 @@ pack_avp(_, Arity, Avp, Acc) -> %% pack_AVP/3 -%% Give Failed-AVP special treatment since it'll contain any -%% unrecognized mandatory AVP's. -pack_AVP(Name, #diameter_avp{is_mandatory = true} = Avp, Acc) - when Name /= 'Failed-AVP' -> - {Rec, Failed} = Acc, - {Rec, [{5001, Avp} | Failed]}; - pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) -> - case avp_arity(Name, 'AVP') of + case pack_arity(Name, M) of 0 -> {Rec, Failed} = Acc, {Rec, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; @@ -326,6 +419,16 @@ pack_AVP(Name, #diameter_avp{is_mandatory = M} = Avp, Acc) -> pack(Arity, 'AVP', Avp, Acc) end. +%% Give Failed-AVP special treatment since it'll contain any +%% unrecognized mandatory AVP's. +pack_arity(Name, M) -> + case Name /= 'Failed-AVP' andalso M andalso is_strict() of + true -> + 0; + false -> + avp_arity(Name, 'AVP') + end. + %% 3588: %% %% DIAMETER_AVP_UNSUPPORTED 5001 diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index 578bbaee2e..9afccf298c 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2013. All Rights Reserved. +# Copyright Ericsson AB 2010-2014. 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 @@ -41,7 +41,7 @@ INCDIR = ../include ABS_EBIN := $(shell cd $(EBIN) && pwd) # Where make should look for dependencies. -VPATH = .:base:compiler:transport:gen +VPATH = .:base:compiler:transport:gen:info # ---------------------------------------------------- # Target specs @@ -55,13 +55,13 @@ DICT_ERLS = $(DICT_MODULES:%=%.erl) DICT_HRLS = $(DICT_MODULES:%=%.hrl) # Modules to build before compiling dictionaries. -COMPILER_MODULES = $(notdir $(filter compiler/%, $(CT_MODULES))) \ - $(DICT_YRL) +COMPILER_MODULES = $(notdir $(CT_MODULES)) $(DICT_YRL) # All handwritten modules from which a depend.mk is generated. MODULES = \ $(RT_MODULES) \ - $(CT_MODULES) + $(CT_MODULES) \ + $(INFO_MODULES) # Modules whose names are inserted into the app file. APP_MODULES = \ @@ -72,6 +72,7 @@ APP_MODULES = \ TARGET_MODULES = \ $(APP_MODULES) \ $(CT_MODULES) \ + $(INFO_MODULES) \ $(DICT_YRL:%=gen/%) # What to build for the 'opt' target. @@ -147,14 +148,19 @@ gen/$(DICT_YRL).erl: compiler/$(DICT_YRL).yrl $(ERLC) -Werror -o $(@D) $< # Generate the app file. -$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk +$(APP_TARGET): $(APP_SRC) ../vsn.mk modules.mk app.sed $(gen_verbose) \ M=`echo $(notdir $(APP_MODULES)) | tr ' ' ,`; \ + C=`echo $(COMPILER_MODULES) | tr ' ' ,`; \ + I=`echo $(notdir $(INFO_MODULES)) | tr ' ' ,`; \ R=`echo $(REGISTERED) | tr ' ' ,`; \ sed -e 's;%VSN%;$(VSN);' \ -e "s;%MODULES%;$$M;" \ + -e "s;%COMPILER%;$$C;" \ + -e "s;%INFO%;$$I;" \ -e "s;%REGISTERED%;$$R;" \ - $< > $@ + $< \ + | sed -f app.sed > $@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk $(vsn_verbose) \ @@ -177,6 +183,8 @@ info: @echo @$(call list,CT_MODULES) @echo + @$(call list,INFO_MODULES) + @echo @$(call list,TARGET_MODULES) @echo @$(call list,TARGET_DIRS) @@ -216,7 +224,7 @@ dialyze: opt $(PLT) -Wno_improper_lists \ $(EBIN)/diameter_gen_base_rfc3588.$(EMULATOR) \ $(patsubst %, $(EBIN)/%.$(EMULATOR), \ - $(notdir $(RT_MODULES) $(CT_MODULES))) + $(notdir $(RT_MODULES) $(CT_MODULES) $(INFO_MODULES))) # Omit all but the common dictionary module since these # (diameter_gen_relay in particular) generate warning depending on how # much of the included diameter_gen.hrl they use. diff --git a/lib/diameter/src/app.sed b/lib/diameter/src/app.sed new file mode 100644 index 0000000000..7916f65002 --- /dev/null +++ b/lib/diameter/src/app.sed @@ -0,0 +1,40 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2014. 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% + +# +# Generate runtime_dependencies from applications to avoid having to +# specify the same application more than once. +# + +/{runtime_dependencies,/b v +/{[-a-z]*, "[0-9.]*"}/!b +/{vsn,/b + +/%%/!H +s/{\([^,]*\)[^}]*}/\1/g +s/%%/%,/ +b + +:v + +p +x +s/\n// +s/%//g +s/\n */ /g +s/{\([^,]*\), "\([^"]*"\)}/"\1-\2/g diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 74086e75b0..4b97fa96b3 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -189,11 +189,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {Mask, Nodes, Dict0, Svc}}) -> putr(?RESTRICT_KEY, Nodes), Tmo = proplists:get_value(capx_timeout, Opts, ?EVENT_TIMEOUT), - ?IS_TIMEOUT(Tmo) orelse ?ERROR({invalid, {capx_timeout, Tmo}}), OnLengthErr = proplists:get_value(length_errors, Opts, exit), - lists:member(OnLengthErr, [exit, handle, discard]) - orelse ?ERROR({invalid, {length_errors, OnLengthErr}}), - %% Error checking is for configuration added in old code. {TPid, Addrs} = start_transport(T, Rest, Svc), @@ -802,10 +798,6 @@ set([_|_] = Ans, FailedAvp) -> result_code(#diameter_header{is_error = true}, _) -> {3008, []}; %% DIAMETER_INVALID_HDR_BITS -result_code(_, [Bs|_]) - when is_bitstring(Bs) -> %% from old code - {3009, []}; %% DIAMETER_INVALID_HDR_BITS - result_code(#diameter_header{version = ?DIAMETER_VERSION}, Es) -> rc(Es); diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 70e66537ed..0dc3eb7123 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -499,9 +499,21 @@ transition(Req, S) -> %% # terminate/2 %% --------------------------------------------------------------------------- -terminate(Reason, #state{service_name = Name} = S) -> +terminate(Reason, #state{service_name = Name, peerT = PeerT} = S) -> send_event(Name, stop), ets:delete(?STATE_TABLE, Name), + + %% Communicate pending loss of any peers that connection_down/3 + %% won't. This is needed when stopping a service since we don't + %% wait for watchdog state changes to take care of if. That this + %% takes place after deleting the state entry ensures that the + %% resulting failover by request processes accomplishes nothing. + ets:foldl(fun(#peer{pid = TPid}, _) -> + diameter_traffic:peer_down(TPid) + end, + ok, + PeerT), + shutdown == Reason %% application shutdown andalso shutdown(application, S). @@ -701,8 +713,7 @@ notify(Share, SvcName, T) -> Nodes = remotes(Share), [] /= Nodes andalso diameter_peer:notify(Nodes, SvcName, T). %% Test for the empty list for upgrade reasons: there's no -%% diameter_peer:notify/3 in old code so no call means no load order -%% requirement. +%% diameter_peer:notify/3 in old code. remotes(false) -> []; @@ -870,7 +881,7 @@ watchdog(TPid, [], ?WD_OKAY, ?WD_SUSPECT = To, Wd, State) -> %% Watchdog has lost its connection. watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{peerT = PeerT} = S) -> - close(Wd, S), + close(Wd), watchdog_down(Wd, To, S), ets:delete(PeerT, TPid); @@ -1188,26 +1199,16 @@ tc(false = No, _, _) -> %% removed %% another watchdog to be able to detect that it should transition %% from initial into reopen rather than okay. That someone is either %% the accepting watchdog upon reception of a CER from the previously -%% connected peer, or us after connect_timer timeout. +%% connected peer, or us after connect_timer timeout or immediately. -close(#watchdog{type = connect}, _) -> +close(#watchdog{type = connect}) -> ok; + close(#watchdog{type = accept, pid = Pid, - ref = Ref, - options = Opts}, - #state{service_name = SvcName}) -> - c(Pid, diameter_config:have_transport(SvcName, Ref), Opts). - -%% Tell watchdog to (maybe) die later ... -c(Pid, true, Opts) -> + options = Opts}) -> Tc = connect_timer(Opts, 2*?DEFAULT_TC), - erlang:send_after(Tc, Pid, close); - -%% ... or now. -c(Pid, false, _Opts) -> - Pid ! close. - + erlang:send_after(Tc, Pid, close). %% The RFC's only document the behaviour of Tc, our connect_timer, %% for the establishment of connections but we also give %% connect_timer semantics for a listener, being the time within @@ -1390,6 +1391,8 @@ pick_peer(Local, Remote, Pid, _SvcName, #diameter_app{mutable = true} = App) case call_service(Pid, {pick_peer, Local, Remote, App}) of {TPid, _} = T when is_pid(TPid) -> T; + false = No -> + No; {error, _} -> false end; diff --git a/lib/diameter/src/base/diameter_stats.erl b/lib/diameter/src/base/diameter_stats.erl index b68d4af11f..8353613d32 100644 --- a/lib/diameter/src/base/diameter_stats.erl +++ b/lib/diameter/src/base/diameter_stats.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -245,9 +245,6 @@ handle_call({read, Refs, Del}, _From, State) -> handle_call({read, Refs}, _, State) -> {reply, read_refs(Refs), State}; -handle_call({flush, Refs}, _From, State) -> %% from old code - {reply, to_refdict(read(Refs, true)), State}; - handle_call(Req, From, State) -> ?UNEXPECTED([Req, From]), {reply, nok, State}. diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index f113d95b2e..f2ac745053 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -94,9 +94,6 @@ make_recvdata([SvcName, PeerT, Apps, Mask | _]) -> peerT = PeerT, apps = Apps, sequence = Mask}. -%% Take a list so that the caller (diameter_service) can be upgraded -%% first if new members are added. Note that receive_message/4 might -%% still get an old term from any watchdog started in old code. %% --------------------------------------------------------------------------- %% peer_up/1 @@ -358,15 +355,6 @@ errors(_, #diameter_packet{header = #diameter_header{version = V}, when V /= ?DIAMETER_VERSION -> Pkt#diameter_packet{errors = [5011 | Es]}; -%% DIAMETER_INVALID_AVP_BITS 3009 -%% A request was received that included an AVP whose flag bits are -%% set to an unrecognized value, or that is inconsistent with the -%% AVP's definition. - -errors(_, #diameter_packet{errors = [Bs | Es]} = Pkt) - when is_bitstring(Bs) -> %% from old code - Pkt#diameter_packet{errors = [3009 | Es]}; - %% DIAMETER_COMMAND_UNSUPPORTED 3001 %% The Request contained a Command-Code that the receiver did not %% recognize or support. This MUST be used when a Diameter node diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index 87b69cc60d..dbfe087659 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -49,8 +49,6 @@ -define(IS_NATURAL(N), (is_integer(N) andalso 0 =< N)). --define(CHOOSE(B,T,F), if (B) -> T; true -> F end). - -record(config, {suspect = 1 :: non_neg_integer(), %% OKAY -> SUSPECT okay = 3 :: non_neg_integer()}). %% REOPEN -> OKAY @@ -157,8 +155,7 @@ wait(Ref, Pid) -> config(Opts) -> Config = proplists:get_value(watchdog_config, Opts, []), - is_list(Config) orelse config_error({watchdog_config, Config}), - lists:foldl(fun config/2, #config{}, Config). %% ^ added in old code + lists:foldl(fun config/2, #config{}, Config). config({suspect, N}, Rec) when ?IS_NATURAL(N) -> @@ -166,10 +163,7 @@ config({suspect, N}, Rec) config({okay, N}, Rec) when ?IS_NATURAL(N) -> - Rec#config{okay = N}; - -config(T, _) -> %% added in old code - config_error(T). + Rec#config{okay = N}. %% start/5 @@ -252,17 +246,6 @@ handle_info(T, #watchdog{} = State) -> ?LOG(stop, T), event(T, State, State#watchdog{status = down}), {stop, {shutdown, T}, State} - end; - -handle_info(T, State) -> %% started in old code - handle_info(T, upgrade(State)). - -upgrade(State) -> - case erlang:append_element(State, #config{}) of - #watchdog{status = okay, config = #config{suspect = OS}} = S -> - S#watchdog{num_dwa = OS}; - #watchdog{} = S -> - S end. close({'DOWN', _, process, TPid, {shutdown, Reason}}, @@ -328,14 +311,13 @@ code_change(_, State, _) -> %% The state transitions documented here are extracted from RFC 3539, %% the commentary is ours. -%% Service or watchdog is telling the watchdog of an accepting -%% transport to die after connect_timer expiry or reestablished -%% connection (in another transport process) respectively. -transition(close, #watchdog{status = down}) -> +%% Service is telling the watchdog of an accepting transport to die +%% following transport death in state INITIAL, or after connect_timer +%% expiry; or another watchdog is saying the same after reestablishing +%% a connection previously had by this one. +transition(close, #watchdog{}) -> {{accept, _}, _, _} = getr(restart), %% assert stop; -transition(close, #watchdog{}) -> - ok; %% Service is asking for the peer to be taken down gracefully. transition({shutdown, Pid, _}, #watchdog{parent = Pid, @@ -418,18 +400,39 @@ transition({open = Key, TPid, _Hosts, T}, %% REOPEN Connection down CloseConnection() %% SetWatchdog() DOWN +%% Transport has died after service requested termination ... transition({'DOWN', _, process, TPid, _Reason}, #watchdog{transport = TPid, shutdown = true}) -> stop; +%% ... or not. transition({'DOWN', _, process, TPid, _Reason}, #watchdog{transport = TPid, - status = T} - = S) -> - set_watchdog(S#watchdog{status = ?CHOOSE(initial == T, T, down), - pending = false, - transport = undefined}); + status = T, + restrict = {_,R}} + = S0) -> + S = S0#watchdog{pending = false, + transport = undefined}, + {{M,_}, _, _} = getr(restart), + + %% Close an accepting watchdog immediately if there's no + %% restriction on the number of connections to the same peer: the + %% state machine never enters state REOPEN in this case. The + %% 'close' message (instead of stop) is so as not to bypass the + %% sending of messages to the service process in handle_info/2. + + if T /= initial, M == accept, not R -> + send(self(), close), + S#watchdog{status = down}; + T /= initial -> + set_watchdog(S#watchdog{status = down}); + M == connect -> + set_watchdog(S); + M == accept -> + send(self(), close), + S + end; %% Incoming message. transition({recv, TPid, Name, Pkt}, #watchdog{transport = TPid} = S) -> @@ -765,7 +768,7 @@ timeout(#watchdog{status = T} = S) restart(#watchdog{transport = undefined} = S) -> restart(getr(restart), S); -restart(S) -> +restart(S) -> %% reconnect has won race with timeout S. %% restart/2 @@ -795,9 +798,10 @@ restart({{connect, _} = T, Opts, Svc}, %% die. Note that a state machine never enters state REOPEN in this %% case. restart({{accept, _}, _, _}, #watchdog{restrict = {_, false}}) -> - stop; + stop; %% 'DOWN' was in old code: 'close' was not sent -%% Otherwise hang around until told to die. +%% Otherwise hang around until told to die, either by the service or +%% by another watchdog. restart({{accept, _}, _, _}, S) -> S. diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index 22422f2ef2..5a068c1a25 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -31,7 +31,8 @@ %% on the beam file of another dictionary. %% --export([from_dict/4]). +-export([from_dict/4, + is_printable_ascii/1]). %% used by ?TERM/1 in diameter_forms.hrl -include("diameter_forms.hrl"). -include("diameter_vsn.hrl"). @@ -121,6 +122,9 @@ eraser(Key) -> %% =========================================================================== %% =========================================================================== +is_printable_ascii(C) -> + 16#20 =< C andalso C =< 16#7F. + get_value(Key, Plist) -> proplists:get_value(Key, Plist, []). diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl index 3941f30e03..136bba16cb 100644 --- a/lib/diameter/src/compiler/diameter_dict_util.erl +++ b/lib/diameter/src/compiler/diameter_dict_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -155,6 +155,8 @@ fmt(grouped_avp_has_wrong_type) -> "Grouped AVP ~s at line ~p defined with type ~s at line ~p"; fmt(grouped_avp_not_defined) -> "Grouped AVP ~s on line ~p not defined in @avp_types"; +fmt(grouped_avp_not_grouped) -> + "Grouped AVP ~s on line ~p not defined in @grouped"; fmt(grouped_vendor_id_without_flag) -> "Grouped AVP ~s at line ~p has vendor id " "but definition at line ~p does not specify V flag"; @@ -401,9 +403,9 @@ read(File) -> {ok, iolist_to_binary([File])}. make_dict(Parse, Opts) -> - make_orddict(pass4(pass3(pass2(pass1(reset(make_dict(Parse), - Opts))), - Opts))). + Dict = pass3(pass2(pass1(reset(make_dict(Parse), Opts))), Opts), + ok = examine(Dict), + make_orddict(Dict). %% make_orddict/1 @@ -1168,7 +1170,7 @@ import_avps(Dict, Opts) -> Import = inherit(Dict, Opts), report(imported, Import), - %% pass4/1 tests that all referenced AVP's are either defined + %% examine/1 tests that all referenced AVP's are either defined %% or imported. dict:store(import_avps, @@ -1276,21 +1278,21 @@ dict(Mod) -> end. %% =========================================================================== -%% pass4/1 +%% examine/1 %% %% Sanity checks. -pass4(Dict) -> - dict:fold(fun(K, V, _) -> p4(K, V, Dict) end, ok, Dict), - Dict. +examine(Dict) -> + dict:fold(fun(K, V, _) -> x(K, V, Dict) end, ok, Dict), + ok. %% Ensure enum AVP's have type Enumerated. -p4({enum, Name}, [Line | _], Dict) +x({enum, Name}, [Line | _], Dict) when is_list(Name) -> true = is_enumerated_avp(Name, Dict, Line); %% Ensure all referenced AVP's are either defined locally or imported. -p4({K, {Name, AvpName}}, [Line | _], Dict) +x({K, {Name, AvpName}}, [Line | _], Dict) when (K == grouped orelse K == messages), is_list(Name), is_list(AvpName), @@ -1298,13 +1300,22 @@ p4({K, {Name, AvpName}}, [Line | _], Dict) true = avp_is_defined(AvpName, Dict, Line); %% Ditto. -p4({K, AvpName}, [Line | _], Dict) +x({K, AvpName}, [Line | _], Dict) when K == avp_vendor_id; K == custom_types; K == codecs -> true = avp_is_defined(AvpName, Dict, Line); -p4(_, _, _) -> +%% Ensure that all local AVP's of type Grouped are also present in @grouped. +x({avp_types, Name}, [Line | Toks], Dict) + when 0 < Line, is_list(Name) -> + [{number, _, _Code}, {word, _, Type}, {word, _, _Flags}] = Toks, + "Grouped" == Type + andalso error == dict:find({grouped, Name}, Dict) + andalso ?RETURN(grouped_avp_not_grouped, [Name, Line]), + ok; + +x(_, _, _) -> ok. %% has_enumerated_type/3 diff --git a/lib/diameter/src/compiler/diameter_forms.hrl b/lib/diameter/src/compiler/diameter_forms.hrl index 9b14c1715a..dd03401b9e 100644 --- a/lib/diameter/src/compiler/diameter_forms.hrl +++ b/lib/diameter/src/compiler/diameter_forms.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -57,4 +57,6 @@ -define(FIELDS(Fs), [{?record_field, ?ATOM(F), V} || {F,V} <- Fs]). %% Literal term. --define(TERM(T), erl_parse:abstract(T, ?LINE)). +-define(TERM(T), erl_parse:abstract(T, [ + {line, ?LINE}, + {encoding, fun diameter_codegen:is_printable_ascii/1}])). diff --git a/lib/diameter/src/compiler/diameter_make.erl b/lib/diameter/src/compiler/diameter_make.erl index 2f314b7e57..72f5d36da4 100644 --- a/lib/diameter/src/compiler/diameter_make.erl +++ b/lib/diameter/src/compiler/diameter_make.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -33,7 +33,8 @@ -export([codec/2, codec/1, format/1, - flatten/1]). + flatten/1, + format_error/1]). -export_type([opt/0]). @@ -81,8 +82,8 @@ codec(File, Opts) -> case parse(Dict, Opts) of {ok, ParseD} -> make(Path, default(Opts), ParseD); - {error = E, Reason} -> - {E, diameter_dict_util:format_error(Reason)} + {error, _} = E -> + E end. codec(File) -> @@ -115,6 +116,11 @@ flatten([?VERSION = V | Dict]) -> [grouped, import_groups], [enum, import_enums]])]. +%% format_error/1 + +format_error(T) -> + diameter_dict_util:format_error(T). + %% =========================================================================== %% flatten/2 @@ -226,21 +232,29 @@ identify([Vsn | [T|_] = ParseD]) identify({path, File} = T) -> {T, File}; identify(File) -> - Bin = iolist_to_binary([File]), - case is_path(Bin) of + case is_path([File]) of true -> {{path, File}, File}; - false -> {Bin, ?DEFAULT_DICT_FILE} + false -> {File, ?DEFAULT_DICT_FILE} end. -%% Interpret anything containing \n or \r as a literal dictionary, -%% otherwise a path. (Which might be the wrong guess in the worst case.) -is_path(Bin) -> - try - [throw(C) || <<C>> <= Bin, $\n == C orelse $\r == C], - true - catch - throw:_ -> false - end. +%% Interpret anything containing \n or \r as a literal dictionary. + +is_path([<<C,B/binary>> | T]) -> + is_path([C, B | T]); + +is_path([[C|L] | T]) -> + is_path([C, L | T]); + +is_path([C|_]) + when $\n == C; + $\r == C -> + false; + +is_path([_|T]) -> + is_path(T); + +is_path([]) -> + true. make(File, Opts, Dict) -> ok(lists:foldl(fun(M,A) -> [make(File, Opts, Dict, M) | A] end, diff --git a/lib/diameter/src/diameter.app.src b/lib/diameter/src/diameter.app.src index ceefb9b398..ac1d847753 100644 --- a/lib/diameter/src/diameter.app.src +++ b/lib/diameter/src/diameter.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -20,9 +20,27 @@ {application, diameter, [{description, "Diameter protocol"}, {vsn, "%VSN%"}, - {modules, [%MODULES%]}, + {modules, [ + %MODULES% + %,%COMPILER% + %,%INFO% + ]}, {registered, [%REGISTERED%]}, - {applications, [stdlib, kernel]}, + {applications, [ + {stdlib, "2.0"}, {kernel, "3.0"}%, {erts, "6.0"} + %% {syntax-tools, "1.6.14"} + %% {runtime-tools, "1.8.14"} + %, {ssl, "5.3.4"} + ]}, {env, []}, - {mod, {diameter_app, []}} + {mod, {diameter_app, []}}, + {runtime_dependencies, [ + ]} + %% + %% Note that ssl is only required if configured on TCP transports, + %% and syntax-tools and runtime-tools are only required if the + %% dictionary compiler and debug modules (respectively) are + %% needed/wanted at runtime, which they typically aren't. These + %% modules are the two commented lines in the 'modules' tuple. + %% ]}. diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src index c7ae8a2828..0d421c229e 100644 --- a/lib/diameter/src/diameter.appup.src +++ b/lib/diameter/src/diameter.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -20,62 +20,37 @@ {"%VSN%", [ - {"0.9", [{restart_application, diameter}]}, %% R14B03 - {"0.10", [{restart_application, diameter}]}, %% R14B04 - {"1.0", [{restart_application, diameter}]}, %% R15B - {"1.1", [{restart_application, diameter}]}, %% R15B01 - {"1.2", [{restart_application, diameter}]}, %% R15B02 - {"1.2.1", [{restart_application, diameter}]}, - {"1.3", [{restart_application, diameter}]}, %% R15B03 - {"1.3.1", [{restart_application, diameter}]}, - {"1.4", [{restart_application, diameter}]}, %% R16A - {"1.4.1", [{restart_application, diameter}]}, %% R16B + {"0.9", [{restart_application, diameter}]}, %% R14B03 + {"0.10", [{restart_application, diameter}]}, %% R14B04 + {"1.0", [{restart_application, diameter}]}, %% R15B + {"1.1", [{restart_application, diameter}]}, %% R15B01 + {"1.2", [{restart_application, diameter}]}, %% R15B02 + {"1.2.1", [{restart_application, diameter}]}, + {"1.3", [{restart_application, diameter}]}, %% R15B03 + {"1.3.1", [{restart_application, diameter}]}, + {"1.4", [{restart_application, diameter}]}, %% R16A + {"1.4.1", [{restart_application, diameter}]}, %% R16B {"1.4.1.1", [{restart_application, diameter}]}, - {"1.4.2", [{load_module, diameter_codec}, %% R16B01 - {load_module, diameter_types}, - {load_module, diameter_config}, - {load_module, diameter_capx}, - {load_module, diameter_service}, - {load_module, diameter_peer_fsm}, - {load_module, diameter_watchdog}, - {load_module, diameter}]}, - {"1.4.3", [{load_module, diameter_capx}, %% R16B02 - {load_module, diameter_service}, - {load_module, diameter_watchdog}, - {load_module, diameter_codec}, - {load_module, diameter_types}, - {load_module, diameter_config}, - {load_module, diameter}]}, - {"1.4.4", [{load_module, diameter_capx}, - {load_module, diameter_service}, - {load_module, diameter_watchdog}, - {load_module, diameter_config}, - {load_module, diameter}]} + {"1.4.2", [{restart_application, diameter}]}, %% R16B01 + {"1.4.3", [{restart_application, diameter}]}, %% R16B02 + {"1.4.4", [{restart_application, diameter}]}, + {"1.5", [{restart_application, diameter}]} %% R16B03 ], [ - {"0.9", [{restart_application, diameter}]}, - {"0.10", [{restart_application, diameter}]}, - {"1.0", [{restart_application, diameter}]}, - {"1.1", [{restart_application, diameter}]}, - {"1.2", [{restart_application, diameter}]}, - {"1.2.1", [{restart_application, diameter}]}, - {"1.3", [{restart_application, diameter}]}, - {"1.3.1", [{restart_application, diameter}]}, - {"1.4", [{restart_application, diameter}]}, - {"1.4.1", [{restart_application, diameter}]}, + {"0.9", [{restart_application, diameter}]}, + {"0.10", [{restart_application, diameter}]}, + {"1.0", [{restart_application, diameter}]}, + {"1.1", [{restart_application, diameter}]}, + {"1.2", [{restart_application, diameter}]}, + {"1.2.1", [{restart_application, diameter}]}, + {"1.3", [{restart_application, diameter}]}, + {"1.3.1", [{restart_application, diameter}]}, + {"1.4", [{restart_application, diameter}]}, + {"1.4.1", [{restart_application, diameter}]}, {"1.4.1.1", [{restart_application, diameter}]}, - {"1.4.2", [{restart_application, diameter}]}, - {"1.4.3", [{load_module, diameter_types}, - {load_module, diameter_config}, - {load_module, diameter_codec}, - {load_module, diameter_service}, - {load_module, diameter_watchdog}, - {load_module, diameter_capx}, - {load_module, diameter}]}, - {"1.4.4", [{load_module, diameter_capx}, - {load_module, diameter_config}, - {load_module, diameter_service}, - {load_module, diameter_watchdog}, - {load_module, diameter}]} + {"1.4.2", [{restart_application, diameter}]}, + {"1.4.3", [{restart_application, diameter}]}, + {"1.4.4", [{restart_application, diameter}]}, + {"1.5", [{restart_application, diameter}]} ] }. diff --git a/lib/diameter/src/base/diameter_dbg.erl b/lib/diameter/src/info/diameter_dbg.erl index 5b0ac3a3b6..b5b3983afa 100644 --- a/lib/diameter/src/base/diameter_dbg.erl +++ b/lib/diameter/src/info/diameter_dbg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -17,20 +17,22 @@ %% %CopyrightEnd% %% +%% +%% Information and debug functions. +%% + -module(diameter_dbg). -export([table/1, tables/0, fields/1, - help/0, modules/0, versions/0, version_info/0, compiled/0, procs/0, latest/0, - nl/0, - log/4]). + nl/0]). -export([diameter_config/0, diameter_peer/0, @@ -52,11 +54,9 @@ tp/1]). -include_lib("diameter/include/diameter.hrl"). --include("diameter_internal.hrl"). - --define(INFO, diameter_info). --define(SEP(), ?INFO:sep()). +-define(APP, diameter). +-define(I, diameter_info). -define(LOCAL, [diameter_config, diameter_peer, @@ -68,27 +68,16 @@ -define(VALUES(Rec), tl(tuple_to_list(Rec))). -log(_Slogan, _Mod, _Line, _Details) -> - ok. - -%%% ---------------------------------------------------------- -%%% # help() -%%% ---------------------------------------------------------- - -help() -> - not_yet_implemented. - -%%% ---------------------------------------------------------- -%%% # table(TableName) -%%% -%%% Input: TableName = diameter table containing record entries. -%%% -%%% Output: Count | undefined -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # table(TableName) +%% +%% Pretty-print a diameter table. Returns the number of records +%% printed, or undefined. +%% ---------------------------------------------------------- table(T) when (T == diameter_peer) orelse (T == diameter_reg) -> - ?INFO:format(collect(T), fields(T), fun ?INFO:split/2); + ?I:format(collect(T), fields(T), fun ?I:split/2); table(Table) when is_atom(Table) -> @@ -96,7 +85,7 @@ table(Table) undefined = No -> No; Fields -> - ?INFO:format(Table, Fields, fun split/2) + ?I:format(Table, Fields, fun split/2) end. split([started, name | Fs], [S, N | Vs]) -> @@ -107,9 +96,9 @@ split([[F|FT]|Fs], [Rec|Vs]) -> split([F|Fs], [V|Vs]) -> {F, Fs, V, Vs}. -%%% ---------------------------------------------------------- -%%% # TableName() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # TableName() +%% ---------------------------------------------------------- -define(TABLE(Name), Name() -> table(Name)). @@ -121,16 +110,15 @@ split([F|Fs], [V|Vs]) -> ?TABLE(diameter_service). ?TABLE(diameter_stats). -%%% ---------------------------------------------------------- -%%% # tables() -%%% -%%% Output: Number of records output. -%%% -%%% Description: Pretty-print records in diameter tables from all nodes. -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # tables() +%% +%% Pretty-print diameter tables from all nodes. Returns the number of +%% records printed. +%% ---------------------------------------------------------- tables() -> - ?INFO:format(field(?LOCAL), fun split/3, fun collect/1). + ?I:format(field(?LOCAL), fun split/3, fun collect/1). field(Tables) -> lists:map(fun(T) -> {T, fields(T)} end, lists:sort(Tables)). @@ -138,66 +126,66 @@ field(Tables) -> split(_, Fs, Vs) -> split(Fs, Vs). -%%% ---------------------------------------------------------- -%%% # modules() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # modules() +%% ---------------------------------------------------------- modules() -> - Path = filename:join([appdir(), atom_to_list(?APPLICATION) ++ ".app"]), - {ok, [{application, ?APPLICATION, Attrs}]} = file:consult(Path), + Path = filename:join([appdir(), atom_to_list(?APP) ++ ".app"]), + {ok, [{application, ?APP, Attrs}]} = file:consult(Path), {modules, Mods} = lists:keyfind(modules, 1, Attrs), Mods. appdir() -> - [_|_] = code:lib_dir(?APPLICATION, ebin). + [_|_] = code:lib_dir(?APP, ebin). -%%% ---------------------------------------------------------- -%%% # versions() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # versions() +%% ---------------------------------------------------------- versions() -> - ?INFO:versions(modules()). + ?I:versions(modules()). -%%% ---------------------------------------------------------- -%%% # versions() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # versions() +%% ---------------------------------------------------------- version_info() -> - ?INFO:version_info(modules()). + ?I:version_info(modules()). -%%% ---------------------------------------------------------- -%%% # compiled() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # compiled() +%% ---------------------------------------------------------- compiled() -> - ?INFO:compiled(modules()). + ?I:compiled(modules()). -%%% ---------------------------------------------------------- -%%% procs() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% procs() +%% ---------------------------------------------------------- procs() -> - ?INFO:procs(?APPLICATION). + ?I:procs(?APP). -%%% ---------------------------------------------------------- -%%% # latest() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # latest() +%% ---------------------------------------------------------- latest() -> - ?INFO:latest(modules()). + ?I:latest(modules()). -%%% ---------------------------------------------------------- -%%% # nl() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # nl() +%% ---------------------------------------------------------- nl() -> lists:foreach(fun(M) -> abcast = c:nl(M) end, modules()). -%%% ---------------------------------------------------------- -%%% # pp(Bin) -%%% -%%% Description: Pretty-print a message binary. -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # pp(Bin) +%% +%% Description: Pretty-print a message binary. +%% ---------------------------------------------------------- %% Network byte order = big endian. @@ -207,7 +195,7 @@ pp(<<Version:8, MsgLength:24, HbHid:32, E2Eid:32, AVPs/binary>>) -> - ?SEP(), + ?I:sep(), ppp(["Version", "Message length", "[Actual length]", @@ -227,7 +215,7 @@ pp(<<Version:8, MsgLength:24, HbHid, E2Eid]), N = avp_loop({AVPs, MsgLength - 20}, 0), - ?SEP(), + ?I:sep(), N; pp(<<_Version:8, MsgLength:24, _/binary>> = Bin) -> @@ -328,23 +316,23 @@ ppp(Fields, Values) -> ppp({Field, Value}) -> io:format(": ~-22s : ~p~n", [Field, Value]). -%%% ---------------------------------------------------------- -%%% # subscriptions() -%%% -%%% Output: list of {SvcName, Pid} -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # subscriptions() +%% +%% Returns a list of {SvcName, Pid}. +%% ---------------------------------------------------------- subscriptions() -> diameter_service:subscriptions(). -%%% ---------------------------------------------------------- -%%% # children() -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- +%% # children() +%% ---------------------------------------------------------- children() -> diameter_sup:tree(). -%%% ---------------------------------------------------------- +%% ---------------------------------------------------------- %% tracer/[12] @@ -430,7 +418,7 @@ peers(Name, Ts) -> mk_peers(Name, [_, {type, connect} | _] = Ts) -> [[Name | mk_peer(Ts)]]; -mk_peers(Name, [R, {type, listen}, O, {accept = A, As}]) -> +mk_peers(Name, [R, {type, listen}, O, {accept = A, As} | _]) -> [[Name | mk_peer([R, {type, A}, O | Ts])] || Ts <- As]. %% This is a bit lame: service_info works to build this list and out %% of something like what we want here and then we take it apart. @@ -485,13 +473,12 @@ fields(diameter_service) -> [started, name, record_info(fields, diameter_service), + watchdogT, peerT, - connT, - share_peers, - use_shared_peers, shared_peers, local_peers, - monitor]; + monitor, + options]; ?FIELDS(diameter_event); ?FIELDS(diameter_uri); diff --git a/lib/diameter/src/base/diameter_info.erl b/lib/diameter/src/info/diameter_info.erl index 39d32d07cd..10972f3231 100644 --- a/lib/diameter/src/base/diameter_info.erl +++ b/lib/diameter/src/info/diameter_info.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -17,6 +17,11 @@ %% %CopyrightEnd% %% +%% +%% Generic functions for formatting table listings and more. Used by +%% diameter_dbg. +%% + -module(diameter_info). -export([usage/1, @@ -573,12 +578,7 @@ sys_info() -> {A,V}. os_info() -> - {os:version(), case os:type() of - {_Fam, _Name} = T -> - T; - Fam -> - {Fam, ""} - end}. + {os:version(), os:type()}. chomp(S) -> string:strip(S, right, $\n). diff --git a/lib/diameter/src/modules.mk b/lib/diameter/src/modules.mk index f8d3cf1d6f..a2a7a51892 100644 --- a/lib/diameter/src/modules.mk +++ b/lib/diameter/src/modules.mk @@ -1,8 +1,7 @@ -#-*-makefile-*- ; force emacs to enter makefile-mode # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2013. All Rights Reserved. +# Copyright Ericsson AB 2010-2014. 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 @@ -64,16 +63,19 @@ RT_MODULES = \ transport/diameter_transport \ transport/diameter_transport_sup -# Handwritten (compile time) modules not included in the app file. +# Handwritten compiler modules not included in the app file. CT_MODULES = \ - base/diameter_dbg \ - base/diameter_info \ compiler/diameter_codegen \ compiler/diameter_exprecs \ compiler/diameter_dict_scanner \ compiler/diameter_dict_util \ compiler/diameter_make +# Info/debug modules, also not included in the app file. +INFO_MODULES = \ + info/diameter_dbg \ + info/diameter_info + # Released hrl files in ../include intended for public consumption. EXTERNAL_HRLS = \ diameter.hrl \ diff --git a/lib/diameter/src/transport/diameter_sctp.erl b/lib/diameter/src/transport/diameter_sctp.erl index 49a530b4eb..d0a01351f3 100644 --- a/lib/diameter/src/transport/diameter_sctp.erl +++ b/lib/diameter/src/transport/diameter_sctp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -70,14 +70,14 @@ -type connect_option() :: {raddr, inet:ip_address()} | {rport, inet:port_number()} - | gen_sctp:open_option(). + | term(). %% gen_sctp:open_option(). -type match() :: inet:ip_address() | string() | [match()]. -type listen_option() :: {accept, match()} - | gen_sctp:open_option(). + | term(). %% gen_sctp:open_option(). -type uint() :: non_neg_integer(). @@ -171,18 +171,33 @@ start_link(T) -> info({gen_sctp, Sock}) -> lists:flatmap(fun(K) -> info(K, Sock) end, - [{socket, sockname}, - {peer, peername}, + [{socket, socknames}, + {peer, peernames}, {statistics, getstat}]). info({K,F}, Sock) -> case inet:F(Sock) of {ok, V} -> - [{K,V}]; + [{K, map(F,V)}]; _ -> [] end. +%% inet:{sock,peer}names/1 returns [{Addr, Port}] but the port number +%% should be the same in each tuple. Map to a {[Addr], Port} tuple if +%% so. +map(K, [{_, Port} | _] = APs) + when K == socknames; + K == peernames -> + try [A || {A,P} <- APs, P == Port orelse throw(?MODULE)] of + As -> {As, Port} + catch + ?MODULE -> APs + end; + +map(_, V) -> + V. + %% --------------------------------------------------------------------------- %% # init/1 %% --------------------------------------------------------------------------- @@ -338,9 +353,6 @@ handle_call({{accept, Ref}, Pid}, _, #listener{ref = Ref, {TPid, NewS} = accept(Ref, Pid, S), {reply, {ok, TPid}, NewS#listener{count = N+1}}; -handle_call(T, From, {listener,_,_,_,_,_,_} = S) -> % started in old code - handle_call(T, From, upgrade(S)); - handle_call(_, _, State) -> {reply, nok, State}. @@ -359,10 +371,7 @@ handle_info(T, #transport{} = S) -> {noreply, #transport{} = t(T,S)}; handle_info(T, #listener{} = S) -> - {noreply, #listener{} = l(T,S)}; - -handle_info(T, {listener,_,_,_,_,_,_} = S) -> % started in old code - handle_info(T, upgrade(S)). + {noreply, #listener{} = l(T,S)}. %% --------------------------------------------------------------------------- %% # code_change/3 @@ -396,9 +405,6 @@ terminate(_, #listener{socket = Sock}) -> %% --------------------------------------------------------------------------- -upgrade(S) -> - #listener{} = erlang:append_element(S, ?DEFAULT_ACCEPT). - putr(Key, Val) -> put({?MODULE, Key}, Val). @@ -502,8 +508,6 @@ transition({peeloff, Sock, {sctp, LSock, _RA, _RP, _Data} = Msg, Matches}, = S) -> ok = accept_peer(Sock, Matches), transition(Msg, S#transport{socket = Sock}); -transition({peeloff = T, _Sock, _Msg} = T, #transport{} = S) ->% from old code - transition(erlang:append_element(T, ?DEFAULT_ACCEPT), S); %% Incoming message. transition({sctp, _Sock, _RA, _RP, Data}, #transport{socket = Sock} = S) -> @@ -560,7 +564,7 @@ accept_peer(_, []) -> ok; accept_peer(Sock, Matches) -> - {RAddrs, _} = ok(inet:peername(Sock)), + RAddrs = [A || {A,_} <- ok(inet:peernames(Sock))], diameter_peer:match(RAddrs, Matches) orelse x({accept, RAddrs, Matches}), ok. @@ -605,11 +609,13 @@ accept(_, Pid, #listener{ref = Ref, pending = {N,Q}} = S) -> %% send/2 %% Outbound Diameter message on a specified stream ... -send(#diameter_packet{bin = Bin, transport_data = {stream, SId}}, S) -> - send(SId, Bin, S), +send(#diameter_packet{bin = Bin, transport_data = {outstream, SId}}, + #transport{streams = {_, OS}} + = S) -> + send(SId rem OS, Bin, S), S; -%% ... or not: rotate through all steams. +%% ... or not: rotate through all streams. send(Bin, #transport{streams = {_, OS}, os = N} = S) diff --git a/lib/diameter/test/diameter_app_SUITE.erl b/lib/diameter/test/diameter_app_SUITE.erl index 1e262895a6..f68a18b5c2 100644 --- a/lib/diameter/test/diameter_app_SUITE.erl +++ b/lib/diameter/test/diameter_app_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -50,7 +50,7 @@ diameter_exprecs, diameter_make]). --define(HELP_MODULES, [diameter_dbg, +-define(INFO_MODULES, [diameter_dbg, diameter_info]). %% =========================================================================== @@ -99,13 +99,13 @@ vsn(Config) -> %% # modules/1 %% %% Ensure that the app file modules and installed modules differ by -%% compiler/help modules. +%% compiler/info modules. %% =========================================================================== modules(Config) -> Mods = fetch(modules, fetch(app, Config)), Installed = code_mods(), - Help = lists:sort(?HELP_MODULES ++ ?COMPILER_MODULES), + Help = lists:sort(?INFO_MODULES ++ ?COMPILER_MODULES), {[], Help} = {Mods -- Installed, lists:sort(Installed -- Mods)}. @@ -158,12 +158,15 @@ appvsn(Name) -> %% # xref/1 %% %% Ensure that no function in our application calls an undefined function -%% or one in an application we haven't specified as a dependency. (Almost.) +%% or one in an application we haven't declared as a dependency. (Almost.) %% =========================================================================== xref(Config) -> App = fetch(app, Config), - Mods = fetch(modules, App), + Mods = fetch(modules, App), %% modules listed in the app file + + %% List of application names extracted from runtime_dependencies. + Deps = lists:map(fun unversion/1, fetch(runtime_dependencies, App)), {ok, XRef} = xref:start(make_name(xref_test_name)), ok = xref:set_default(XRef, [{verbose, false}, {warnings, false}]), @@ -178,7 +181,9 @@ xref(Config) -> [?APP, erts | fetch(applications, App)]), {ok, Undefs} = xref:analyze(XRef, undefined_function_calls), - {ok, Called} = xref:analyze(XRef, {module_call, ?COMPILER_MODULES}), + {ok, RTmods} = xref:analyze(XRef, {module_use, Mods}), + {ok, CTmods} = xref:analyze(XRef, {module_use, ?COMPILER_MODULES}), + {ok, RTdeps} = xref:analyze(XRef, {module_call, Mods}), xref:stop(XRef), @@ -190,18 +195,41 @@ xref(Config) -> Undefs), %% diameter_tcp does call ssl despite the latter not being listed %% as a dependency in the app file since ssl is only required for - %% TLS security: it's up to a client who wants TLS it to start - %% ssl. - - [] = lists:filter(fun is_bad_dependency/1, Called). - -%% It's not strictly necessary that diameter compiler modules not -%% depend on other diameter modules but it's a simple source of build -%% errors if not encoded in the makefile (hence the test) so guard -%% against it. -is_bad_dependency(Mod) -> - lists:prefix("diameter", atom_to_list(Mod)) - andalso not lists:member(Mod, ?COMPILER_MODULES). + %% TLS security: it's up to a client who wants TLS to start ssl. + + %% Ensure that only runtime or info modules call runtime modules. + %% It's not strictly necessary that diameter compiler modules not + %% depend on other diameter modules but it's a simple source of + %% build errors if not properly encoded in the makefile so guard + %% against it. + [] = (RTmods -- Mods) -- ?INFO_MODULES, + + %% Ensure that runtime modules don't call compiler modules. + CTmods = CTmods -- Mods, + + %% Ensure that runtime modules only call other runtime modules, or + %% applications declared as in runtime_dependencies in the app + %% file. Note that the declared application versions are ignored + %% since we only know what we can see now. + [] = lists:filter(fun(M) -> not lists:member(app(M), Deps) end, + RTdeps -- Mods). + +unversion(App) -> + T = lists:dropwhile(fun is_vsn_ch/1, lists:reverse(App)), + lists:reverse(case T of [$-|TT] -> TT; _ -> T end). + +is_vsn_ch(C) -> + $0 =< C andalso C =< $9 orelse $. == C. + +app('$M_EXPR') -> %% could be anything but assume it's ok + "erts"; +app(Mod) -> + case code:which(Mod) of + preloaded -> + "erts"; + Path -> + unversion(lists:nth(3, lists:reverse(filename:split(Path)))) + end. add_application(XRef, App) -> add_application(XRef, App, code:lib_dir(App)). diff --git a/lib/diameter/test/diameter_codec_test.erl b/lib/diameter/test/diameter_codec_test.erl index 0b4568a9e5..90536dcf2b 100644 --- a/lib/diameter/test/diameter_codec_test.erl +++ b/lib/diameter/test/diameter_codec_test.erl @@ -1,8 +1,7 @@ -%% coding: utf-8 %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 diff --git a/lib/diameter/test/diameter_compiler_SUITE.erl b/lib/diameter/test/diameter_compiler_SUITE.erl index ed369e8af3..08ffe5981d 100644 --- a/lib/diameter/test/diameter_compiler_SUITE.erl +++ b/lib/diameter/test/diameter_compiler_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -120,6 +120,16 @@ {avp_has_duplicate_flag, " -", " MM"}, + {ok, + "@vendor 0", + "@vendor 10415"}, + {ok, + [{"@vendor 0", "@vendor 10415"}, + {"Proxy-Info .*M$", "&V"}, + {"Proxy-Info ::= [^>]*", "& 10415 "}]}, + {grouped_vendor_id_without_flag, + [{"@vendor 0", "@vendor 10415"}, + {"Proxy-Info ::= [^>]*", "& 10415 "}]}, {avp_has_vendor_id, "@avp_types", "@avp_vendor_id 667 Class\n&"}, @@ -138,6 +148,9 @@ {grouped_avp_not_defined, "Failed-AVP *.*", ""}, + {grouped_avp_not_grouped, + "Failed-AVP ::=.*\n.*}", + ""}, {grouped_vendor_id_without_flag, "(Failed-AVP .*)>", "\\1 668>"}, @@ -397,8 +410,8 @@ replace({E, Mods}, Bin) -> case {E, parse(B, [{include, here()}]), Mods} of {ok, {ok, Dict}, _} -> Dict; - {_, {error, S}, _} -> - S + {_, {error, {E,_} = T}, _} when E /= ok -> + diameter_make:format_error(T) end. re({RE, Repl}, Bin) -> diff --git a/lib/diameter/test/diameter_config_SUITE.erl b/lib/diameter/test/diameter_config_SUITE.erl index 46ff63756d..d10ee83ba4 100644 --- a/lib/diameter/test/diameter_config_SUITE.erl +++ b/lib/diameter/test/diameter_config_SUITE.erl @@ -1,4 +1,3 @@ -%% coding: utf-8 %% %% %CopyrightBegin% %% diff --git a/lib/diameter/test/diameter_dpr_SUITE.erl b/lib/diameter/test/diameter_dpr_SUITE.erl index 9252650bf7..f3f16b06e0 100644 --- a/lib/diameter/test/diameter_dpr_SUITE.erl +++ b/lib/diameter/test/diameter_dpr_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2012. All Rights Reserved. +%% Copyright Ericsson AB 2012-2014. 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 @@ -73,7 +73,7 @@ %% Valid values for Disconnect-Cause. -define(CAUSES, [0, rebooting, 1, busy, 2, goaway]). -%% Establish one client connection for element of this list, +%% Establish one client connection for each element of this list, %% configured with disconnect/5 as disconnect_cb and returning the %% specified value. -define(RETURNS, @@ -129,8 +129,8 @@ stop_service(Config) -> service == group(Config) andalso (ok = diameter:stop_service(?CLIENT)). -%% Check for callbacks and stop the service. (Not the other way around -%% for the timing reason explained below.) +%% Check for callbacks before diameter:stop/0, not the other way around +%% for the timing reason explained below. check(Config) -> Grp = group(Config), [Pid | Refs] = ?util:read_priv(Config, config), diff --git a/lib/diameter/test/diameter_failover_SUITE.erl b/lib/diameter/test/diameter_failover_SUITE.erl index dfd3253827..c1494dcdb1 100644 --- a/lib/diameter/test/diameter_failover_SUITE.erl +++ b/lib/diameter/test/diameter_failover_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2013. All Rights Reserved. +%% Copyright Ericsson AB 2010-2014. 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 @@ -47,6 +47,7 @@ send_discard_1/1, send_discard_2/1, stop_services/1, + empty/1, stop/1]). %% diameter callbacks @@ -121,6 +122,7 @@ all() -> send_discard_1, send_discard_2, stop_services, + empty, stop]. %% =========================================================================== @@ -147,6 +149,10 @@ stop_services(_Config) -> T <- [diameter:stop_service(H)], T /= ok]. +%% Ensure transports have been removed from request table. +empty(_Config) -> + [] = ets:tab2list(diameter_request). + stop(_Config) -> ok = diameter:stop(). diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk index 9fda067f2b..54019fa46c 100644 --- a/lib/diameter/vsn.mk +++ b/lib/diameter/vsn.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2013. All Rights Reserved. +# Copyright Ericsson AB 2010-2014. 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 @@ -18,5 +18,5 @@ # %CopyrightEnd% APPLICATION = diameter -DIAMETER_VSN = 1.5 +DIAMETER_VSN = 1.6 APP_VSN = $(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN) |