From e0603ba18a67c1ef33f60122fe6f00393c0c0203 Mon Sep 17 00:00:00 2001 From: Anders Svensson Date: Thu, 13 Jul 2017 01:28:06 +0200 Subject: Tweak map-valued decode Use the same [MsgName | Avps] representation as for the list decode, but with Avps a map instead of a AVP name/values list. As a result, don't set the message/AVP name on an additional key in the map, which felt a bit odd. Messages are [MsgName :: atom() | map()], Grouped AVPs are just map(). Fix at least one problem in the traffic suite along the way: with decode_format false, the own decode in to_map/2 didn't know whether or not to decode strings, resulting on some failures. --- lib/diameter/doc/src/diameter.xml | 12 +- lib/diameter/doc/src/diameter_codec.xml | 8 +- lib/diameter/src/base/diameter_codec.erl | 7 +- lib/diameter/src/base/diameter_gen.erl | 13 +- lib/diameter/src/base/diameter_traffic.erl | 58 ++--- lib/diameter/test/diameter_traffic_SUITE.erl | 332 ++++++++++++--------------- 6 files changed, 193 insertions(+), 237 deletions(-) diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index a7f001e096..43cb3a538c 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -539,7 +539,7 @@ that matches no peer.

The host and realm filters cause the Destination-Host and Destination-Realm AVPs to be extracted from the -outgoing request, assuming it to be a record-, list- or map-valued +outgoing request, assuming it to be a record- or list-valued &codec_message;, and assuming at most one of each AVP. If this is not the case then the {host|realm, &dict_DiameterIdentity;} filters must be used to achieve the desired result. @@ -802,10 +802,16 @@ be matched by corresponding &capability; configuration, of {decode_format, record | list | map | false}

-The type of decoded messages and grouped AVPs in the msg field +The format of decoded messages and grouped AVPs in the msg field of diameter_packet records and value field of diameter_avp records respectively. -If false then the fields are set this value. +If record then a record whose definition is generated from the +dictionary file in question. +If list or map then a [Name | Avps] pair where +Avps is either a list of AVP name/values pairs or a map keyed on +AVP names respectively. +If false then the representation is omitted and msg and +value fields are set to false. See also &codec_message;.

diff --git a/lib/diameter/doc/src/diameter_codec.xml b/lib/diameter/doc/src/diameter_codec.xml index 4df8e788b7..0846334d23 100644 --- a/lib/diameter/doc/src/diameter_codec.xml +++ b/lib/diameter/doc/src/diameter_codec.xml @@ -230,7 +230,8 @@ header.

-message() = record() | list() | map() +message() = record() + | maybe_improper_list()

The representation of a Diameter message as passed to @@ -240,9 +241,8 @@ a message as defined in a dictionary file is encoded as a record with one field for each component AVP. Equivalently, a message can also be encoded as a list whose head is the atom-valued message name (as specified in the relevant dictionary -file) and whose tail is a list of {AvpName, AvpValues} pairs, -or as a map with values keyed on AVP names and the message name in key -:name. +file) and whose tail is either a list of AVP name/values +pairs or a map with values keyed on AVP names. The format at decode is determined by &mod_service_opt; decode_format. Any of the formats is accepted at encode.

diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 275e80b9bb..9b21bf4141 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -275,9 +275,6 @@ rec2msg(_, [Name|_]) when is_atom(Name) -> Name; -rec2msg(_, #{':name' := Name}) -> - Name; - rec2msg(Mod, Rec) -> Mod:rec2msg(element(1, Rec)). @@ -383,7 +380,9 @@ decode_avps(MsgName, Mod, AppMod, Opts, Pkt, Avps) -> %% ... or not errors = Errors, avps = As}. -reformat(MsgName, Avps, #{decode_format := list}) -> +reformat(MsgName, Avps, #{decode_format := T}) + when T == map; + T == list -> [MsgName | Avps]; reformat(_, Msg, _) -> diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index 78d8bd2fa3..597ec143a8 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -186,7 +186,7 @@ decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> %% AM counts the number of top-level AVPs, which missing/4 then %% uses when adding 5005 errors. Arities = Mod:avp_arity(Name), - {reformat(Rec, Arities, Mod, Fmt), + {reformat(Name, Rec, Arities, Mod, Fmt), Avps, Failed ++ missing(Arities, Opts, Mod, AM)}. @@ -752,8 +752,8 @@ newrec(_, _, false = No) -> newrec(Mod, Name, record) -> newrec(Mod, Name); -newrec(_, Name, _) -> - #{':name' => Name}. +newrec(_, _, _) -> + #{}. %% newrec/2 @@ -762,15 +762,14 @@ newrec(Mod, Name) -> %% reformat/4 -reformat(Map, Arities, _Mod, list) -> +reformat(_, Map, Arities, _Mod, list) -> [{F,V} || {F,_} <- Arities, #{F := V} <- [Map]]; -reformat(Map, Arities, Mod, record_from_map) -> - #{':name' := Name} = Map, +reformat(Name, Map, Arities, Mod, record_from_map) -> RecName = Mod:name2rec(Name), list_to_tuple([RecName | [maps:get(F, Map, def(A)) || {F,A} <- Arities]]); -reformat(Rec, _, _, _) -> +reformat(_, Rec, _, _, _) -> Rec. %% def/1 diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index f684f60cb7..e9c422d6ab 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -621,16 +621,10 @@ is_answer_message(#diameter_packet{msg = Msg}, Dict0) -> is_answer_message([#diameter_header{is_request = R, is_error = E} | _], _) -> E andalso not R; -%% Message sent as a tagged avp/value list. +%% Message sent as a map or tagged avp/value list. is_answer_message([Name | _], _) -> Name == 'answer-message'; -%% Message sent as a map. -is_answer_message(Map, _) - when is_map(Map) -> - #{':name' := Name} = Map, - Name == 'answer-message'; - %% Message sent as a record. is_answer_message(Rec, Dict) -> try @@ -875,15 +869,13 @@ reset(Msg, [RC | Avps], Dict) -> %% set/3 -%% Reply as name and tuple list ... +%% Reply as name/values list ... +set([Name|As], Avps, _) + when is_map(As) -> + [Name | maps:merge(As, maps:from_list(Avps))]; set([_|_] = Ans, Avps, _) -> Ans ++ Avps; %% Values nearer tail take precedence. -%% ... a map ... -set(Ans, Avps, _) - when is_map(Ans) -> - maps:merge(Ans, maps:from_list(Avps)); - %% ... or record. set(Rec, Avps, Dict) -> Dict:'#set-'(Avps, Rec). @@ -902,9 +894,6 @@ rc([MsgName | _], RC, Dict) -> _ -> [] end; -rc(#{':name' := Name}, RC, Dict) -> - rc([Name], RC, Dict); - rc(Rec, RC, Dict) -> rc([Dict:rec2msg(element(1, Rec))], RC, Dict). @@ -937,10 +926,6 @@ failed(Msg, FailedAvp, Dict) -> msg2rec([MsgName | _], Dict) -> Dict:msg2rec(MsgName); -%% ... map ... -msg2rec(#{':name' := MsgName}, Dict) -> - Dict:msg2rec(MsgName); - %% ... or record. msg2rec(Rec, _) -> element(1, Rec). @@ -949,12 +934,11 @@ msg2rec(Rec, _) -> %% Message as name/values list ... values([_ | Avps], F, _) -> - proplists:get_value(F, Avps, []); - -%% ... map ... -values(Msg, F, _) - when is_map(Msg) -> - maps:get(F, Msg, []); + if is_map(Avps) -> + maps:get(F, Avps, []); + is_list(Avps) -> + proplists:get_value(F, Avps, []) + end; %% ... or record. values(Rec, F, Dict) -> @@ -1906,23 +1890,13 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) -> %% Message as name/values list ... get_avp(_, Name, [_MsgName | Avps]) -> - case lists:keyfind(Name, 1, Avps) of + case find(Name, Avps) of {_, V} -> #diameter_avp{name = Name, value = V}; _ -> undefined end; -%% ... map ... -get_avp(_, Name, Map) - when is_map(Map) -> - case maps:find(Name, Map) of - {ok, V} -> - #diameter_avp{name = Name, value = V}; - error -> - undefined - end; - %% ... or record (but not necessarily). get_avp(Dict, Name, Rec) -> try @@ -1932,6 +1906,16 @@ get_avp(Dict, Name, Rec) -> undefined end. +%% find/2 + +find(Key, Map) + when is_map(Map) -> + maps:find(Key, Map); + +find(Key, List) + when is_list(List) -> + lists:keyfind(Key, 1, List). + %% get_avp_value/3 get_avp_value(Dict, Name, Msg) -> diff --git a/lib/diameter/test/diameter_traffic_SUITE.erl b/lib/diameter/test/diameter_traffic_SUITE.erl index 100a4eebc9..fae9f86c38 100644 --- a/lib/diameter/test/diameter_traffic_SUITE.erl +++ b/lib/diameter/test/diameter_traffic_SUITE.erl @@ -99,14 +99,14 @@ stop/1]). %% diameter callbacks --export([peer_up/3, - peer_down/3, - pick_peer/6, pick_peer/7, - prepare_request/5, prepare_request/6, - prepare_retransmit/5, - handle_answer/6, handle_answer/7, - handle_error/6, - handle_request/3]). +-export([peer_up/4, + peer_down/4, + pick_peer/7, pick_peer/8, + prepare_request/6, prepare_request/7, + prepare_retransmit/6, + handle_answer/7, handle_answer/8, + handle_error/7, + handle_request/4]). %% diameter_{tcp,sctp} callbacks -export([message/3]). @@ -182,19 +182,17 @@ %% A common match when receiving answers in a client. -define(answer_message(SessionId, ResultCode), - #{':name' := 'answer-message', - 'Session-Id' := SessionId, - 'Origin-Host' := _, - 'Origin-Realm' := _, - 'Result-Code' := ResultCode}). + ['answer-message' | #{'Session-Id' := SessionId, + 'Origin-Host' := _, + 'Origin-Realm' := _, + 'Result-Code' := ResultCode}]). -define(answer_message(ResultCode), - #{':name' := 'answer-message', - 'Origin-Host' := _, - 'Origin-Realm' := _, - 'Result-Code' := ResultCode}). + ['answer-message' | #{'Origin-Host' := _, + 'Origin-Realm' := _, + 'Result-Code' := ResultCode}]). %% Config for diameter:start_service/2. --define(SERVICE(Name, Decode), +-define(SERVICE(Name, Grp), [{'Origin-Host', Name ++ "." ++ ?REALM}, {'Origin-Realm', ?REALM}, {'Host-IP-Address', [?ADDR]}, @@ -203,10 +201,10 @@ {'Auth-Application-Id', [0]}, %% common messages {'Acct-Application-Id', [3]}, %% base accounting {restrict_connections, false}, - {string_decode, Decode}, + {string_decode, Grp#group.strings}, {incoming_maxlen, 1 bsl 21} | [{application, [{dictionary, D}, - {module, ?MODULE}, + {module, [?MODULE, Grp]}, {answer_errors, callback}]} || D <- [diameter_gen_base_rfc3588, diameter_gen_base_accounting, @@ -446,15 +444,15 @@ start(_Config) -> ok = diameter:start(). start_services(Config) -> - #group{strings = S, - client_service = CN, + #group{client_service = CN, server_service = SN, server_decoding = SD} + = Grp = group(Config), ok = diameter:start_service(SN, [{decode_format, SD} - | ?SERVICE(SN, S)]), + | ?SERVICE(SN, Grp)]), ok = diameter:start_service(CN, [{sequence, ?CLIENT_MASK} - | ?SERVICE(CN, S)]). + | ?SERVICE(CN, Grp)]). add_transports(Config) -> #group{transport = T, @@ -484,8 +482,6 @@ add_transports(Config) -> R <- ?RFCS, R /= rfc4005 orelse have_nas(), Id <- [{D,E}]], - %% The server uses the client's Origin-State-Id to decide how to - %% answer. ?util:write_priv(Config, "transport", [LRef | Cs]). server_apps() -> @@ -567,9 +563,8 @@ send_ok(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 1}], - #{':name' := 'ACA', - 'Result-Code' := ?SUCCESS, - 'Session-Id' := _} + ['ACA' | #{'Result-Code' := ?SUCCESS, + 'Session-Id' := _}] = call(Config, Req). %% Send an accounting ACR that the server answers badly to. @@ -585,9 +580,8 @@ send_eval(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 3}], - #{':name' := 'ACA', - 'Result-Code' := ?SUCCESS, - 'Session-Id' := _} + ['ACA' | #{'Result-Code' := ?SUCCESS, + 'Session-Id' := _}] = call(Config, Req). %% Send an accounting ACR that the server tries to answer with an @@ -613,8 +607,7 @@ send_protocol_error(Config) -> send_experimental_result(Config) -> Req = ['ACR', {'Accounting-Record-Type', ?EVENT_RECORD}, {'Accounting-Record-Number', 5}], - #{':name' := 'ACA', - 'Session-Id' := _} + ['ACA' | #{'Session-Id' := _}] = call(Config, Req). %% Send an ASR with an arbitrary non-mandatory AVP and expect success @@ -622,11 +615,10 @@ send_experimental_result(Config) -> send_arbitrary(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{name = 'Product-Name', value = "XXX"}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS, - 'AVP' := [#diameter_avp{name = 'Product-Name', - value = V}]} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS, + 'AVP' := [#diameter_avp{name = 'Product-Name', + value = V}]}] = call(Config, Req), "XXX" = string(V, Config). @@ -635,12 +627,11 @@ send_unknown(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = false, data = <<17>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS, - 'AVP' := [#diameter_avp{code = 999, - is_mandatory = false, - data = <<17>>}]} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS, + 'AVP' := [#diameter_avp{code = 999, + is_mandatory = false, + data = <<17>>}]}] = call(Config, Req). %% Ditto, and point the AVP length past the end of the message. Expect @@ -652,10 +643,9 @@ send_unknown_short(Config, M, RC) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = M, data = <<17>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := RC, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := RC, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{code = 999, is_mandatory = M, @@ -667,10 +657,9 @@ send_unknown_mandatory(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 999, is_mandatory = true, data = <<17>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?AVP_UNSUPPORTED, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{code = 999, is_mandatory = true, @@ -688,10 +677,9 @@ send_unexpected_mandatory_decode(Config) -> Req = ['ASR', {'AVP', [#diameter_avp{code = 27, %% Session-Timeout is_mandatory = true, data = <<12:32>>}]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?AVP_UNSUPPORTED, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{code = 27, is_mandatory = true, @@ -707,10 +695,9 @@ send_unexpected_mandatory_decode(Config) -> send_grouped_error(Config) -> Req = ['ASR', {'Proxy-Info', [[{'Proxy-Host', "abcd"}, {'Proxy-State', ""}]]}], - #{':name' := 'ASA', - 'Session-Id' := _, - 'Result-Code' := ?INVALID_AVP_LENGTH, - 'Failed-AVP' := Avps} + ['ASA' | #{'Session-Id' := _, + 'Result-Code' := ?INVALID_AVP_LENGTH, + 'Failed-AVP' := Avps}] = call(Config, Req), [[#diameter_avp{name = 'Proxy-Info', value = V}]] = failed_avps(Avps, Config), @@ -743,9 +730,8 @@ send_error_bit(Config) -> %% Send a bad version and check that we get 5011. send_unsupported_version(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?UNSUPPORTED_VERSION} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?UNSUPPORTED_VERSION}] = call(Config, Req). %% Send a request containing an AVP length > data size. @@ -765,12 +751,11 @@ send_zero_avp_length(Config) -> send_invalid_avp_length(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?INVALID_AVP_LENGTH, - 'Origin-Host' := _, - 'Origin-Realm' := _, - 'Failed-AVP' := Avps} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?INVALID_AVP_LENGTH, + 'Origin-Host' := _, + 'Origin-Realm' := _, + 'Failed-AVP' := Avps}] = call(Config, Req), [[_]] = failed_avps(Avps, Config). @@ -787,18 +772,16 @@ send_invalid_reject(Config) -> send_unexpected_mandatory(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?AVP_UNSUPPORTED} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?AVP_UNSUPPORTED}] = call(Config, Req). %% Send something long that will be fragmented by TCP. send_long(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}, {'User-Name', [binary:copy(<<$X>>, 1 bsl 20)]}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req). %% Send something longer than the configure incoming_maxlen. @@ -841,9 +824,8 @@ send_any_2(Config) -> send_all_1(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], Realm = lists:foldr(fun(C,A) -> [C,A] end, [], ?REALM), - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req, [{filter, {all, [{host, any}, {realm, Realm}]}}]). send_all_2(Config) -> @@ -872,9 +854,8 @@ send_detach(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], Ref = make_ref(), ok = call(Config, Req, [{extra, [{self(), Ref}]}, detach]), - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = receive {Ref, T} -> T end. %% Send a request which can't be encoded and expect {error, encode}. @@ -887,15 +868,13 @@ send_destination_1(Config) -> = group(Config), Req = ['STR', {'Termination-Cause', ?LOGOUT}, {'Destination-Host', [?HOST(SN, ?REALM)]}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req, [{filter, {all, [host, realm]}}]). send_destination_2(Config) -> Req = ['STR', {'Termination-Cause', ?LOGOUT}], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, Req, [{filter, {all, [host, realm]}}]). %% Send with filtering on and expect failure when specifying an @@ -959,9 +938,8 @@ send_bad_filter(Config, F) -> %% Specify multiple filter options and expect them be conjunctive. send_multiple_filters_1(Config) -> Fun = fun(#diameter_caps{}) -> true end, - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = send_multiple_filters(Config, [host, {eval, Fun}]). send_multiple_filters_2(Config) -> E = {erlang, is_tuple, []}, @@ -972,9 +950,8 @@ send_multiple_filters_3(Config) -> E2 = {erlang, is_tuple, []}, E3 = {erlang, is_record, [diameter_caps]}, E4 = [{erlang, is_record, []}, diameter_caps], - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = send_multiple_filters(Config, [{eval, E} || E <- [E1,E2,E3,E4]]). send_multiple_filters(Config, Fs) -> @@ -985,9 +962,8 @@ send_multiple_filters(Config, Fs) -> %% only the return value from the prepare_request callback being %% significant. send_anything(Config) -> - #{':name' := 'STA', - 'Session-Id' := _, - 'Result-Code' := ?SUCCESS} + ['STA' | #{'Session-Id' := _, + 'Result-Code' := ?SUCCESS}] = call(Config, anything). %% =========================================================================== @@ -1037,12 +1013,11 @@ call(Config, Req, Opts) -> #group{encoding = Enc, client_service = CN, client_dict = Dict0} - = Group = group(Config), diameter:call(CN, dict(Req, Dict0), msg(Req, Enc, Dict0), - [{extra, [{Name, Group}, diameter_lib:now()]} | Opts]). + [{extra, [Name, diameter_lib:now()]} | Opts]). origin({D,E}) -> 4*decode(D) + encode(E); @@ -1082,32 +1057,37 @@ msg([H|_] = Msg, record = E, diameter_gen_base_rfc6733) msg([H|T], record, Dict) -> Dict:'#new-'(Dict:msg2rec(H), T); -msg([H|T], map, _) -> - Map = maps:from_list(T), - Map#{':name' => H}; +msg([H|As], map, _) + when is_list(As) -> + [H | maps:from_list(As)]; msg(Msg, _, _) -> Msg. -to_map(map, #diameter_packet{msg = Msg}) - when is_map(Msg) -> +to_map(#diameter_packet{msg = [_MsgName | Avps] = Msg}, + #group{server_decoding = map}) + when is_map(Avps) -> Msg; -to_map(list, #diameter_packet{msg = [MsgName | Avps]}) -> - maps:put(':name', MsgName, maps:from_list(Avps)); +to_map(#diameter_packet{msg = [MsgName | Avps]}, + #group{server_decoding = list}) -> + [MsgName | maps:from_list(Avps)]; -to_map(record, #diameter_packet{header = H, msg = Rec}) -> +to_map(#diameter_packet{header = H, msg = Rec}, + #group{server_decoding = record}) -> rec_to_map(Rec, dict(H)); %% No record decode: do it ourselves. -to_map(false, #diameter_packet{header = H, - msg = false, - bin = Bin}) -> +to_map(#diameter_packet{header = H, + msg = false, + bin = Bin}, + #group{server_decoding = false, + strings = B}) -> Opts = #{decode_format => map, - string_decode => false, + string_decode => B, strict_mbit => true, rfc => 6733}, - #diameter_packet{msg = Msg} + #diameter_packet{msg = [_MsgName | _Map] = Msg} = diameter_codec:decode(dict(H), Opts, Bin), Msg. @@ -1123,11 +1103,9 @@ dict(#diameter_header{application_id = Id, rec_to_map(Rec, Dict) -> [R | Vs] = Dict:'#get-'(Rec), - maps:put(':name', - Dict:rec2msg(R), - maps:from_list([T || {_,V} = T <- Vs, - V /= undefined, - V /= []])). + [Dict:rec2msg(R) | maps:from_list([T || {_,V} = T <- Vs, + V /= undefined, + V /= []])]. appdict(rfc4005) -> nas4005; @@ -1166,12 +1144,12 @@ acct(diameter_gen_base_rfc6733) -> %% Set only values that aren't already. -set(_, [H|T], Vs) -> - [H | Vs ++ T]; - -set(_, Map, Vs) - when is_map(Map) -> - maps:merge(maps:from_list(Vs), Map); +set(_, [N | As], Vs) -> + [N | if is_map(As) -> + maps:merge(maps:from_list(Vs), As); + is_list(As) -> + Vs ++ As + end]; set(#group{client_dict = Dict0} = _Group, Rec, Vs) -> Dict = dict(Rec, Dict0), @@ -1192,26 +1170,26 @@ reset(_, _, _, Rec) -> %% =========================================================================== %% diameter callbacks -%% peer_up/3 +%% peer_up/4 -peer_up(_SvcName, _Peer, State) -> +peer_up(_SvcName, _Peer, State, _Group) -> State. %% peer_down/3 -peer_down(_SvcName, _Peer, State) -> +peer_down(_SvcName, _Peer, State, _Group) -> State. -%% pick_peer/6-7 +%% pick_peer/7-8 -pick_peer(Peers, _, [$C|_], _State, {Name, Group}, _) +pick_peer(Peers, _, [$C|_], _State, Group, Name, _) when Name /= send_detach -> find(Group, Peers). -pick_peer(_Peers, _, [$C|_], _State, {send_nopeer, _}, _, ?EXTRA) -> +pick_peer(_Peers, _, [$C|_], _State, _Group, send_nopeer, _, ?EXTRA) -> false; -pick_peer(Peers, _, [$C|_], _State, {send_detach, Group}, _, {_,_}) -> +pick_peer(Peers, _, [$C|_], _State, Group, send_detach, _, {_,_}) -> find(Group, Peers). find(#group{encoding = E, @@ -1227,15 +1205,15 @@ id(Id, {Pid, _Caps}, SvcName) -> = diameter:service_info(SvcName, Pid), lists:member({id, Id}, Opts). -%% prepare_request/5-6 +%% prepare_request/6-7 -prepare_request(_Pkt, [$C|_], {_Ref, _Caps}, {send_discard, _}, _) -> +prepare_request(_Pkt, [$C|_], {_Ref, _Caps}, _, send_discard, _) -> {discard, unprepared}; -prepare_request(Pkt, [$C|_], {_Ref, Caps}, {Name, Group}, _) -> +prepare_request(Pkt, [$C|_], {_Ref, Caps}, Group, Name, _) -> {send, prepare(Pkt, Caps, Name, Group)}. -prepare_request(Pkt, [$C|_], {_Ref, Caps}, {send_detach, Group}, _, _) -> +prepare_request(Pkt, [$C|_], {_Ref, Caps}, Group, send_detach, _, _) -> {eval_packet, {send, prepare(Pkt, Caps, Group)}, [fun log/2, detach]}. log(#diameter_packet{bin = Bin} = P, T) @@ -1446,12 +1424,12 @@ set(N, #diameter_packet{msg = Req}, Caps, Group) %% name/1 +name([H|#{}]) -> + {map, H}; + name([H|_]) -> {list, H}; -name(#{} = Map) -> - {map, maps:get(':name', Map)}; - name(Rec) -> try {record, element(1, Rec)} @@ -1460,17 +1438,17 @@ name(Rec) -> false end. -%% prepare_retransmit/5 +%% prepare_retransmit/6 -prepare_retransmit(_Pkt, false, _Peer, _Name, _Group) -> +prepare_retransmit(_Pkt, false, _Peer, _Group, _Name, _) -> discard. -%% handle_answer/6-7 +%% handle_answer/7-8 -handle_answer(Pkt, Req, [$C|_], Peer, {Name, Group}, _) -> +handle_answer(Pkt, Req, [$C|_], Peer, Group, Name, _) -> answer(Pkt, Req, Peer, Name, Group). -handle_answer(Pkt, Req, [$C|_], Peer, {send_detach = Name, Group}, _, X) -> +handle_answer(Pkt, Req, [$C|_], Peer, Group, send_detach = Name, _, X) -> {Pid, Ref} = X, Pid ! {Ref, answer(Pkt, Req, Peer, Name, Group)}. @@ -1503,18 +1481,18 @@ app(Req, _, Dict0) -> Dict = dict(Req, Dict0), Dict:id(). -%% handle_error/6 +%% handle_error/7 -handle_error(timeout = Reason, _Req, [$C|_], _Peer, _, Time) -> +handle_error(timeout = Reason, _Req, [$C|_], _Peer, _, _, Time) -> Now = diameter_lib:now(), {Reason, {diameter_lib:timestamp(Time), diameter_lib:timestamp(Now), diameter_lib:micro_diff(Now, Time)}}; -handle_error(Reason, _Req, [$C|_], _Peer, _, _Time) -> +handle_error(Reason, _Req, [$C|_], _Peer, _, _, _Time) -> {error, Reason}. -%% handle_request/3 +%% handle_request/4 %% Note that diameter will set Result-Code and Failed-AVPs if %% #diameter_packet.errors is non-null. @@ -1522,7 +1500,10 @@ handle_error(Reason, _Req, [$C|_], _Peer, _, _Time) -> handle_request(#diameter_packet{header = H, avps = As} = Pkt, _, - {_Ref, Caps}) -> + {_Ref, Caps}, + #group{encoding = E, + server_decoding = D} + = Grp) -> #diameter_header{end_to_end_id = EI, hop_by_hop_id = HI} = H, @@ -1530,8 +1511,8 @@ handle_request(#diameter_packet{header = H, avps = As} V = EI bsr B, %% assert V = HI bsr B, %% #diameter_caps{origin_state_id = {_,[Id]}} = Caps, - {D,_} = T = origin(Id), - wrap(T, H, request(to_map(D, Pkt), [H|As], Caps)). + {D,E} = T = origin(Id), %% assert + wrap(T, H, request(to_map(Pkt, Grp), [H|As], Caps)). wrap(Id, H, {Tag, Action, Post}) -> {Tag, wrap(Id, H, Action), Post}; @@ -1585,8 +1566,7 @@ base_to_nas(Msg, _) -> %% request/3 %% send_experimental_result -request(#{':name' := 'ACR', - 'Accounting-Record-Number' := 5}, +request(['ACR' | #{'Accounting-Record-Number' := 5}], [Hdr | Avps], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> @@ -1619,16 +1599,14 @@ request(Msg, _Avps, Caps) -> %% request/2 %% send_nok -request(#{':name' := 'ACR', - 'Accounting-Record-Number' := 0}, +request(['ACR' | #{'Accounting-Record-Number' := 0}], _) -> {eval_packet, {protocol_error, ?INVALID_AVP_BITS}, [fun log/2, invalid]}; %% send_bad_answer -request(#{':name' := 'ACR', - 'Session-Id' := SId, - 'Accounting-Record-Type' := RT, - 'Accounting-Record-Number' := 2 = RN}, +request(['ACR' | #{'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 2 = RN}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['ACA', {'Result-Code', ?SUCCESS}, @@ -1642,10 +1620,9 @@ request(#{':name' := 'ACR', msg = Ans}}; %% send_eval -request(#{':name' := 'ACR', - 'Session-Id' := SId, - 'Accounting-Record-Type' := RT, - 'Accounting-Record-Number' := 3 = RN}, +request(['ACR' | #{'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 3 = RN}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['ACA', {'Result-Code', ?SUCCESS}, @@ -1657,10 +1634,9 @@ request(#{':name' := 'ACR', {eval, {reply, Ans}, {erlang, now, []}}; %% send_ok -request(#{':name' := 'ACR', - 'Session-Id' := SId, - 'Accounting-Record-Type' := RT, - 'Accounting-Record-Number' := 1 = RN}, +request(['ACR' | #{'Session-Id' := SId, + 'Accounting-Record-Type' := RT, + 'Accounting-Record-Number' := 1 = RN}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ACA', {'Result-Code', ?SUCCESS}, @@ -1671,8 +1647,7 @@ request(#{':name' := 'ACR', {'Accounting-Record-Number', RN}]}; %% send_protocol_error -request(#{':name' := 'ACR', - 'Accounting-Record-Number' := 4}, +request(['ACR' | #{'Accounting-Record-Number' := 4}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> Ans = ['answer-message', {'Result-Code', ?TOO_BUSY}, @@ -1680,46 +1655,39 @@ request(#{':name' := 'ACR', {'Origin-Realm', OR}], {reply, Ans}; -request(#{':name' := 'ASR', - 'Session-Id' := SId} - = Req, +request(['ASR' | #{'Session-Id' := SId} = Avps], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['ASA', {'Result-Code', ?SUCCESS}, {'Session-Id', SId}, {'Origin-Host', OH}, {'Origin-Realm', OR}, - {'AVP', maps:get('AVP', Req, [])}]}; + {'AVP', maps:get('AVP', Avps, [])}]}; %% send_invalid_reject -request(#{':name' := 'STR', - 'Termination-Cause' := ?USER_MOVED}, +request(['STR' | #{'Termination-Cause' := ?USER_MOVED}], _Caps) -> {protocol_error, ?TOO_BUSY}; %% send_noreply -request(#{':name' := 'STR', - 'Termination-Cause' := T}, +request(['STR' | #{'Termination-Cause' := T}], _Caps) when T /= ?LOGOUT -> discard; %% send_destination_5 -request(#{':name' := 'STR', - 'Destination-Realm' := R}, +request(['STR' | #{'Destination-Realm' := R}], #diameter_caps{origin_realm = {OR, _}}) when R /= undefined, R /= OR -> {protocol_error, ?REALM_NOT_SERVED}; %% send_destination_6 -request(#{':name' := 'STR', - 'Destination-Host' := [H]}, +request(['STR' | #{'Destination-Host' := [H]}], #diameter_caps{origin_host = {OH, _}}) when H /= OH -> {protocol_error, ?UNABLE_TO_DELIVER}; -request(#{':name' := 'STR', - 'Session-Id' := SId}, +request(['STR' | #{'Session-Id' := SId}], #diameter_caps{origin_host = {OH, _}, origin_realm = {OR, _}}) -> {reply, ['STA', {'Result-Code', ?SUCCESS}, @@ -1728,7 +1696,7 @@ request(#{':name' := 'STR', {'Origin-Realm', OR}]}; %% send_error/send_timeout -request(#{':name' := 'RAR'}, _Caps) -> +request(['RAR' | #{}], _Caps) -> receive after 2000 -> {protocol_error, ?TOO_BUSY} end. %% message/3 -- cgit v1.2.3