diff options
Diffstat (limited to 'lib/diameter/src/base')
-rw-r--r-- | lib/diameter/src/base/diameter.erl | 9 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_codec.erl | 15 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_config.erl | 9 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_gen.erl | 372 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_peer_fsm.erl | 9 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_service.erl | 1 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_traffic.erl | 82 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_watchdog.erl | 9 |
8 files changed, 333 insertions, 173 deletions
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index bd92e16fba..85a54c8e61 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -47,6 +47,7 @@ stop/0]). -export_type([evaluable/0, + decode_format/0, restriction/0, message_length/0, remotes/0, @@ -330,6 +331,13 @@ call(SvcName, App, Message) -> -type message_length() :: 0..16#FFFFFF. +-type decode_format() + :: record + | list + | map + | false + | record_from_map. + %% Options passed to start_service/2 -type service_opt() @@ -338,6 +346,7 @@ call(SvcName, App, Message) -> | {restrict_connections, restriction()} | {sequence, sequence() | evaluable()} | {share_peers, remotes()} + | {decode_format, decode_format()} | {string_decode, boolean()} | {strict_mbit, boolean()} | {incoming_maxlen, message_length()} diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 82fa796e69..9b21bf4141 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -29,7 +29,7 @@ msg_name/2, msg_id/1]). -%% Towards generated encoders (from diameter_gen.hrl). +%% towards diameter_gen -export([pack_data/2, pack_avp/2]). @@ -287,7 +287,8 @@ rec2msg(Mod, Rec) -> %% longer *the* decode. decode(Mod, Pkt) -> - Opts = #{string_decode => true, + Opts = #{decode_format => record, + string_decode => true, strict_mbit => true, rfc => 6733}, decode(Mod, Opts, Pkt). @@ -375,10 +376,18 @@ decode_avps(MsgName, Mod, AppMod, Opts, Pkt, Avps) -> %% ... or not Opts#{dictionary => AppMod, failed_avp => false}), ?LOGC([] /= Errors, decode_errors, Pkt#diameter_packet.header), - Pkt#diameter_packet{msg = Rec, + Pkt#diameter_packet{msg = reformat(MsgName, Rec, Opts), errors = Errors, avps = As}. +reformat(MsgName, Avps, #{decode_format := T}) + when T == map; + T == list -> + [MsgName | Avps]; + +reformat(_, Msg, _) -> + Msg. + %%% --------------------------------------------------------------------------- %%% # decode_header/1 %%% --------------------------------------------------------------------------- diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index 34018ae6d3..d591fa903e 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -713,6 +713,7 @@ make_config(SvcName, Opts) -> {nodes, restrict_connections}, {16#FFFFFF, incoming_maxlen}, {true, strict_mbit}, + {record, decode_format}, {true, string_decode}, {[], spawn_opt}]), @@ -756,6 +757,7 @@ opt(K, false = B) K == monitor; K == restrict_connections; K == strict_mbit; + K == decode_format; K == string_decode -> B; @@ -766,6 +768,13 @@ opt(K, true = B) K == string_decode -> B; +opt(decode_format, T) + when T == record; + T == list; + T == map; + T == record_from_map -> + T; + opt(restrict_connections, T) when T == node; T == nodes -> diff --git a/lib/diameter/src/base/diameter_gen.erl b/lib/diameter/src/base/diameter_gen.erl index e832832876..a7dc3aaec3 100644 --- a/lib/diameter/src/base/diameter_gen.erl +++ b/lib/diameter/src/base/diameter_gen.erl @@ -50,7 +50,9 @@ %% # encode_avps/3 %% --------------------------------------------------------------------------- --spec encode_avps(parent_name(), parent_record() | avp_values(), map()) +-spec encode_avps(parent_name(), + parent_record() | avp_values() | map(), + map()) -> iolist() | no_return(). @@ -83,6 +85,11 @@ encode(Name, Vals, Opts, Mod) when is_list(Vals) -> encode(Name, Mod:'#set-'(Vals, newrec(Mod, Name)), Opts, Mod); +encode(Name, Map, Opts, Mod) + when is_map(Map) -> + [enc(Name, F, A, V, Opts, Mod) || {F,A} <- Mod:avp_arity(Name), + V <- [maps:get(F, Map, def(A))]]; + encode(Name, Rec, Opts, Mod) -> [encode(Name, F, V, Opts, Mod) || {F,V} <- Mod:'#get-'(Rec)]. @@ -97,7 +104,8 @@ enc(_, AvpName, 1, undefined, _, _) -> ?THROW([mandatory_avp_missing, AvpName]); enc(Name, AvpName, 1, Value, Opts, Mod) -> - enc(Name, AvpName, [Value], Opts, Mod); + H = avp_header(AvpName, Mod), + enc1(Name, AvpName, H, Value, Opts, Mod); enc(_, _, {0,_}, [], _, _) -> []; @@ -106,31 +114,51 @@ enc(_, AvpName, _, T, _, _) when not is_list(T) -> ?THROW([repeated_avp_as_non_list, AvpName, T]); -enc(_, AvpName, {Min, _}, L, _, _) - when length(L) < Min -> - ?THROW([repeated_avp_insufficient_arity, AvpName, Min, L]); +enc(Name, AvpName, {Min, Max}, Values, Opts, Mod) -> + H = avp_header(AvpName, Mod), + enc(Name, AvpName, H, Min, 0, Max, Values, Opts, Mod). -enc(_, AvpName, {_, Max}, L, _, _) - when Max < length(L) -> - ?THROW([repeated_avp_excessive_arity, AvpName, Max, L]); +%% enc/9 -enc(Name, AvpName, _, Values, Opts, Mod) -> - enc(Name, AvpName, Values, Opts, Mod). +enc(_, AvpName, _, Min, N, _, [], _, _) + when N < Min -> + ?THROW([repeated_avp_insufficient_arity, AvpName, Min, N]); -%% enc/5 +enc(_, _, _, _, _, _, [], _, _) -> + []; -enc(Name, 'AVP', Values, Opts, Mod) -> - [enc_AVP(Name, A, Opts, Mod) || A <- Values]; +enc(Name, AvpName, H, Min, N, '*', Vs, Opts, Mod) + when Min =< N -> + [enc1(Name, AvpName, H, V, Opts, Mod) || V <- Vs]; -enc(_, AvpName, Values, Opts, Mod) -> - enc(AvpName, Values, Opts, Mod). +enc(_, AvpName, _, _, N, Max, _, _, _) + when Max =< N -> + ?THROW([repeated_avp_excessive_arity, AvpName, Max]); -%% enc/4 +enc(Name, AvpName, H, Min, N, Max, [V|Vs], Opts, Mod) -> + [enc1(Name, AvpName, H, V, Opts, Mod) + | enc(Name, AvpName, H, Min, N+1, Max, Vs, Opts, Mod)]. + +%% avp_header/2 + +avp_header('AVP', _) -> + false; -enc(AvpName, Values, Opts, Mod) -> - H = Mod:avp_header(AvpName), - [diameter_codec:pack_data(H, Mod:avp(encode, V, AvpName, Opts)) - || V <- Values]. +avp_header(AvpName, Mod) -> + {_,_,_} = Mod:avp_header(AvpName). + +%% enc1/6 + +enc1(Name, 'AVP', false, Value, Opts, Mod) -> + enc_AVP(Name, Value, Opts, Mod); + +enc1(_, AvpName, Hdr, Value, Opts, Mod) -> + enc1(AvpName, Hdr, Value, Opts, Mod). + +%% enc1/5 + +enc1(AvpName, {_,_,_} = Hdr, Value, Opts, Mod) -> + diameter_codec:pack_data(Hdr, Mod:avp(encode, Value, AvpName, Opts)). %% enc_AVP/4 @@ -153,16 +181,21 @@ enc_AVP(_, #diameter_avp{name = N, value = V}, _, _) enc_AVP(Name, #diameter_avp{name = AvpName, value = Data}, Opts, Mod) -> 0 == Mod:avp_arity(Name, AvpName) orelse ?THROW([known_avp_as_AVP, Name, AvpName, Data]), - enc(AvpName, [Data], Opts, Mod); + enc(AvpName, Data, Opts, Mod); %% The backdoor ... enc_AVP(_, {AvpName, Value}, Opts, Mod) -> - enc(AvpName, [Value], Opts, Mod); + enc(AvpName, Value, Opts, Mod); %% ... and the side door. enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> diameter_codec:pack_avp(#diameter_avp{data = T}, Opts). +%% enc/4 + +enc(AvpName, Value, Opts, Mod) -> + enc1(AvpName, Mod:avp_header(AvpName), Value, Opts, Mod). + %% --------------------------------------------------------------------------- %% # decode_avps/3 %% --------------------------------------------------------------------------- @@ -171,15 +204,21 @@ enc_AVP(_Name, {_Dict, _AvpName, _Data} = T, Opts, _) -> -> {parent_record(), [avp()], Failed} when Failed :: [{5000..5999, #diameter_avp{}}]. -decode_avps(Name, Recs, #{module := Mod} = Opts) -> - {Avps, {Rec, Failed}} +decode_avps(Name, Recs, #{module := Mod, decode_format := Fmt} = Opts) -> + {Avps, {Rec, AM, Failed}} = mapfoldl(fun(T,A) -> decode(Name, Opts, Mod, T, A) end, - {newrec(Mod, Name), []}, + {newrec(Mod, Name, Fmt), #{}, []}, Recs), - {Rec, Avps, Failed ++ missing(Rec, Name, Failed, Opts, Mod)}. + %% AM counts the number of top-level AVPs, which missing/4 then + %% uses when adding 5005 errors. + Arities = Mod:avp_arity(Name), + {reformat(Name, Rec, Arities, Mod, Fmt), + Avps, + Failed ++ missing(Arities, Opts, Mod, AM)}. + %% Append 5005 errors so that errors are reported in the order %% encountered. Failed-AVP should typically contain the first -%% encountered error accordg to the RFC. +%% error encountered. %% mapfoldl/3 %% @@ -194,6 +233,8 @@ mapfoldl(F, Acc0, [T|Rest], List) -> mapfoldl(_, Acc, [], List) -> {List, Acc}. +%% missing/4 + %% 3588: %% %% DIAMETER_MISSING_AVP 5005 @@ -204,57 +245,41 @@ mapfoldl(_, Acc, [], List) -> %% Vendor-Id if applicable. The value field of the missing AVP %% should be of correct minimum length and contain zeros. -missing(Rec, Name, Failed, Opts, Mod) -> - Avps = lists:foldl(fun({_, #diameter_avp{code = C, vendor_id = V}}, A) -> - maps:put({C,V}, true, A) - end, - maps:new(), - Failed), - missing(Mod:avp_arity(Name), tl(tuple_to_list(Rec)), Avps, Opts, Mod, []). - -missing([{Name, Arity} | As], [Value | Vs], Avps, Opts, Mod, Acc) -> - missing(As, - Vs, - Avps, - Opts, - Mod, - case - [H || missing_arity(Arity, Value), - {C,_,V} = H <- [Mod:avp_header(Name)], - not maps:is_key({C,V}, Avps)] - of - [H] -> - [{5005, empty_avp(Name, H, Opts, Mod)} | Acc]; - [] -> - Acc - end); - -missing([], [], _, _, _, Acc) -> - Acc. - -%% Maximum arities have already been checked in building the record. - -missing_arity(1, V) -> - V == undefined; -missing_arity({0, _}, _) -> - false; -missing_arity({1, _}, L) -> - [] == L; -missing_arity({Min, _}, L) -> - not has_prefix(Min, L). - -%% Compare a non-negative integer and the length of a list without -%% computing the length. -has_prefix(0, _) -> - true; -has_prefix(_, []) -> +missing(Arities, Opts, Mod, AM) -> + lists:foldl(fun(T,A) -> missing(T, AM, Opts, Mod, A) end, + [], + Arities). + +%% missing/5 + +missing({Name, Arity}, AM, Opts, Mod, Acc) -> + case missing(Name, Arity, AM) of + true -> [{5005, empty_avp(Name, Opts, Mod)} | Acc]; + false -> Acc + end. + +%% missing/3 + +missing(Name, Arity, AM) -> + 'AVP' /= Name andalso too_few(Name, Arity, AM). + +%% too_few/3 +%% +%% Maximum arities have already been checked during the decode. + +too_few(_, {0, _}, _) -> false; -has_prefix(N, [_|L]) -> - has_prefix(N-1, L). -%% empty_avp/4 +too_few(FieldName, 1, AM) -> + not maps:is_key(FieldName, AM); + +too_few(FieldName, {M, _}, AM) -> + maps:get(FieldName, AM, 0) < M. -empty_avp(Name, {Code, Flags, VId}, Opts, Mod) -> +%% empty_avp/3 + +empty_avp(Name, Opts, Mod) -> + {Code, Flags, VId} = Mod:avp_header(Name), {Name, Type} = Mod:avp_name(Code, VId), #diameter_avp{name = Name, code = Code, @@ -280,8 +305,27 @@ decode(Name, Mod, #diameter_avp{code = Code, vendor_id = Vid} = Avp, - Acc) -> - decode(Name, Opts, Mod, Mod:avp_name(Code, Vid), Avp, Acc). + {Rec, AM, Failed}) -> + T = Mod:avp_name(Code, Vid), + decode(Name, Opts, Mod, T, Avp, {Rec, incr(field(T), AM), Failed}). + +%% field/1 + +field({AvpName, _Type}) -> + AvpName; + +field('AVP' = A) -> + A. + +%% incr/2 + +incr(Key, Map) -> + maps:update_with(Key, fun incr/1, 1, Map). + +%% incr/1 + +incr(N) -> + N + 1. %% decode/6 @@ -352,15 +396,13 @@ decode(Name, Opts0, Mod, {AvpName, Type}, Avp, Acc) -> %% decode is packed into 'AVP'. %% Reset the dictionary for best-effort decode of Failed-AVP. - DecMod = if Failed -> - AppMod; - true -> - Mod + DecMod = if Failed -> AppMod; + true -> Mod end, - %% On decode, a Grouped AVP is represented as a #diameter_avp{} - %% list with AVP as head and component AVPs as tail. On encode, - %% data can be a list of component AVPs. + %% A Grouped AVP is represented as a #diameter_avp{} list with AVP + %% as head and component AVPs as tail. On encode, data can be a + %% list of component AVPs. try avp_decode(Data, AvpName, Opts, DecMod, Mod) of {Rec, As} when Type == 'Grouped' -> @@ -425,7 +467,7 @@ trim(Avp) -> %% decode_error/7 -decode_error(Name, [_ | Rec], _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> +decode_error(Name, [_|Rec], _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> decode_AVP(Name, Avp#diameter_avp{value = Rec}, Opts, Mod, Acc); decode_error(Name, _, _, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> @@ -437,24 +479,25 @@ decode_error(_, [Error | _], ComponentAvps, _, _, Avp, Acc) -> decode_error(_, Error, ComponentAvps, _, _, Avp, Acc) -> decode_error(Error, Avp, Acc, ComponentAvps). -%% decode_error/5 +%% decode_error/6 decode_error(Name, _Reason, #{failed_avp := true} = Opts, Mod, Avp, Acc) -> decode_AVP(Name, Avp, Opts, Mod, Acc); -decode_error(Name, Reason, Opts, Mod, Avp, {Rec, Failed}) -> +decode_error(Name, Reason, Opts, Mod, Avp, {Rec, AM, Failed}) -> Stack = diameter_lib:get_stacktrace(), + AvpName = Avp#diameter_avp.name, diameter_lib:log(decode_error, ?MODULE, ?LINE, - {Reason, Name, Avp#diameter_avp.name, Mod, Stack}), - {Avp, {Rec, [rc(Reason, Avp, Opts, Mod) | Failed]}}. + {Reason, Name, AvpName, Mod, Stack}), + {Avp, {Rec, AM, [rc(Reason, Avp, Opts, Mod) | Failed]}}. %% decode_error/4 -decode_error({RC, ErrorData}, Avp, {Rec, Failed}, ComponentAvps) -> +decode_error({RC, ErrorData}, Avp, {Rec, AM, Failed}, ComponentAvps) -> E = Avp#diameter_avp{data = [ErrorData]}, - {[Avp | trim(ComponentAvps)], {Rec, [{RC, E} | Failed]}}. + {[Avp | trim(ComponentAvps)], {Rec, AM, [{RC, E} | Failed]}}. %% set_strict/3 @@ -490,8 +533,8 @@ decode_AVP(Name, Avp, Opts, Mod, Acc) -> %% DIAMETER_INVALID_AVP_LENGTH (5014). A module specified to a %% @custom_types tag in a dictionary file can also raise an error of %% this form. -rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = Avp, Opts, Mod) -> - {RC, Avp#diameter_avp{data = Mod:empty_value(AvpName, Opts)}}; +rc({'DIAMETER', 5014 = RC, _}, #diameter_avp{name = AvpName} = A, Opts, Mod) -> + {RC, A#diameter_avp{data = Mod:empty_value(AvpName, Opts)}}; %% 3588: %% @@ -531,20 +574,33 @@ pack_avp(_, Arity, #diameter_avp{name = AvpName} = Avp, _Opts, Mod, Acc) -> %% type. pack_AVP(_, #diameter_avp{data = {5014 = RC, Data}} = Avp, _, _, Acc) -> - {Rec, Failed} = Acc, - {Rec, [{RC, Avp#diameter_avp{data = Data}} | Failed]}; + {Rec, AM, Failed} = Acc, + {Rec, AM, [{RC, Avp#diameter_avp{data = Data}} | Failed]}; pack_AVP(Name, Avp, Opts, Mod, Acc) -> - pack_arity(Name, pack_arity(Name, Opts, Mod, Avp), Avp, Mod, Acc). - -%% pack_arity/5 + Arity = pack_arity(Name, Opts, Mod, Avp), + if 0 == Arity -> + M = Avp#diameter_avp.is_mandatory, + {Rec, AM, Failed} = Acc, + {Rec, AM, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; + true -> + pack(Arity, 'AVP', Avp, Mod, Acc) + end. -pack_arity(_, 0, #diameter_avp{is_mandatory = M} = Avp, _, Acc) -> - {Rec, Failed} = Acc, - {Rec, [{if M -> 5001; true -> 5008 end, Avp} | Failed]}; +%% 3588: +%% +%% DIAMETER_AVP_UNSUPPORTED 5001 +%% The peer received a message that contained an AVP that is not +%% recognized or supported and was marked with the Mandatory bit. A +%% Diameter message with this error MUST contain one or more Failed- +%% AVP AVP containing the AVPs that caused the failure. +%% +%% DIAMETER_AVP_NOT_ALLOWED 5008 +%% A message was received with an AVP that MUST NOT be present. The +%% Failed-AVP AVP MUST be included and contain a copy of the +%% offending AVP. -pack_arity(_, Arity, Avp, Mod, Acc) -> - pack(Arity, 'AVP', Avp, Mod, Acc). +%% pack_arity/4 %% Give Failed-AVP special treatment since (1) it'll contain any %% unrecognized mandatory AVP's and (2) the RFC 3588 grammar failed to @@ -573,31 +629,13 @@ pack_arity(Name, 0 end. -%% 3588: -%% -%% DIAMETER_AVP_UNSUPPORTED 5001 -%% The peer received a message that contained an AVP that is not -%% recognized or supported and was marked with the Mandatory bit. A -%% Diameter message with this error MUST contain one or more Failed- -%% AVP AVP containing the AVPs that caused the failure. -%% -%% DIAMETER_AVP_NOT_ALLOWED 5008 -%% A message was received with an AVP that MUST NOT be present. The -%% Failed-AVP AVP MUST be included and contain a copy of the -%% offending AVP. - %% pack/5 -pack(Arity, FieldName, Avp, Mod, {Rec, _} = Acc) -> - pack(Mod:'#get-'(FieldName, Rec), Arity, FieldName, Avp, Mod, Acc). - -%% pack/6 - -pack(undefined, 1, 'AVP' = F, Avp, Mod, {Rec, Failed}) -> %% unlikely - {Mod:'#set-'({F, Avp}, Rec), Failed}; - -pack(undefined, 1, F, #diameter_avp{value = V}, Mod, {Rec, Failed}) -> - {Mod:'#set-'({F, V}, Rec), Failed}; +pack(Arity, F, Avp, Mod, {Rec, AM, Failed}) -> + case too_many(F, Arity, AM) of + true -> {Rec, AM, [{5009, Avp} | Failed]}; + false -> {set(Arity, F, value(F, Avp), Mod, Rec), AM, Failed} + end. %% 3588: %% @@ -608,18 +646,45 @@ pack(undefined, 1, F, #diameter_avp{value = V}, Mod, {Rec, Failed}) -> %% the offending AVP that exceeded the maximum number of occurrences %% -pack(_, 1, _, Avp, _, {Rec, Failed}) -> - {Rec, [{5009, Avp} | Failed]}; +%% too_many/3 -pack(L, {_, Max}, F, Avp, Mod, {Rec, Failed}) -> - case '*' /= Max andalso has_prefix(Max+1, L) of - true -> - {Rec, [{5009, Avp} | Failed]}; - false when F == 'AVP' -> - {Mod:'#set-'({F, [Avp | L]}, Rec), Failed}; - false -> - {Mod:'#set-'({F, [Avp#diameter_avp.value | L]}, Rec), Failed} - end. +too_many(_, {_, '*'}, _) -> + false; + +too_many(FieldName, {_, M}, Map) -> + too_many(FieldName, M, Map); + +too_many(FieldName, M, Map) -> + #{FieldName := N} = Map, + M < N. + +%% set/5 + +set(_, _, _, _, false = No) -> + No; + +set(1, F, Value, _, Map) + when is_map(Map) -> + maps:put(F, Value, Map); + +set(_, F, V, _, Map) + when is_map(Map) -> + maps:update_with(F, fun(Vs) -> [V|Vs] end, [V], Map); + +set(1, F, Value, Mod, Rec) -> + Mod:'#set-'({F, Value}, Rec); + +set(_, F, V, Mod, Rec) -> + Vs = Mod:'#get-'(F, Rec), + Mod:'#set-'({F, [V|Vs]}, Rec). + +%% value/2 + +value('AVP', Avp) -> + Avp; + +value(_, #diameter_avp{value = V}) -> + V. %% --------------------------------------------------------------------------- %% # grouped_avp/3 @@ -705,5 +770,38 @@ empty(Name, #{module := Mod} = Opts) -> %% ------------------------------------------------------------------------------ +%% newrec/3 + +newrec(_, _, false = No) -> + No; + +newrec(Mod, Name, record) -> + newrec(Mod, Name); + +newrec(_, _, _) -> + #{}. + +%% newrec/2 + newrec(Mod, Name) -> Mod:'#new-'(Mod:name2rec(Name)). + +%% reformat/4 + +reformat(_, Map, Arities, _Mod, list) -> + [{F,V} || {F,_} <- Arities, #{F := V} <- [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, _, _, _) -> + Rec. + +%% def/1 + +def(1) -> + undefined; + +def(_) -> + []. diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index e43b3f54cf..597e395c62 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.erl @@ -128,7 +128,8 @@ %% outgoing DPR; boolean says whether or not %% the request was sent explicitly with %% diameter:call/4. - codec :: #{string_decode := boolean(), + codec :: #{decode_format := record, + string_decode := boolean(), strict_mbit := boolean(), rfc := 3588 | 6733, ordered_encode := false}, @@ -253,11 +254,13 @@ i({Ack, WPid, {M, Ref} = T, Opts, {SvcOpts, Nodes, Dict0, Svc}}) -> length_errors = LengthErr, strict = Strictness, incoming_maxlen = Maxlen, - codec = maps:with([string_decode, + codec = maps:with([decode_format, + string_decode, strict_mbit, rfc, ordered_encode], - SvcOpts#{ordered_encode => false})}. + SvcOpts#{ordered_encode => false, + decode_format => record})}. %% The transport returns its local ip addresses so that different %% transports on the same service can use different local addresses. %% The local addresses are put into Host-IP-Address avps here when diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 788f697627..cbb7549e39 100644 --- a/lib/diameter/src/base/diameter_service.erl +++ b/lib/diameter/src/base/diameter_service.erl @@ -113,6 +113,7 @@ restrict_connections := diameter:restriction(), incoming_maxlen := diameter:message_length(), strict_mbit := boolean(), + decode_format := diameter:decode_format(), string_decode := boolean(), spawn_opt := list() | {module(), atom(), list()}}}). diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl index 85378babea..e9c422d6ab 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -76,7 +76,8 @@ service_name :: diameter:service_name(), apps :: [#diameter_app{}], sequence :: diameter:sequence(), - codec :: #{string_decode := boolean(), + codec :: #{decode_format := diameter:decode_format(), + string_decode := boolean(), strict_mbit := boolean(), incoming_maxlen := diameter:message_length()}}). %% Note that incoming_maxlen is currently handled in diameter_peer_fsm, @@ -102,7 +103,8 @@ make_recvdata([SvcName, PeerT, Apps, SvcOpts | _]) -> peerT = PeerT, apps = Apps, sequence = Mask, - codec = maps:with([string_decode, + codec = maps:with([decode_format, + string_decode, strict_mbit, ordered_encode, incoming_maxlen], @@ -619,7 +621,7 @@ 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'; @@ -867,7 +869,10 @@ 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. @@ -900,33 +905,44 @@ failed_avp(_, [] = No, _) -> failed_avp(Msg, [_|_] = Avps, Dict) -> [failed(Msg, [{'AVP', Avps}], Dict)]. -%% Reply as name and tuple list ... -failed([MsgName | Values], FailedAvp, Dict) -> - RecName = Dict:msg2rec(MsgName), +%% failed/3 + +failed(Msg, FailedAvp, Dict) -> + RecName = msg2rec(Msg, Dict), try - Dict:'#info-'(RecName, {index, 'Failed-AVP'}), + Dict:'#info-'(RecName, {index, 'Failed-AVP'}), %% assert existence {'Failed-AVP', [FailedAvp]} catch error: _ -> - Avps = proplists:get_value('AVP', Values, []), + Avps = values(Msg, 'AVP', Dict), A = #diameter_avp{name = 'Failed-AVP', value = FailedAvp}, {'AVP', [A|Avps]} + end. + +%% msg2rec/2 + +%% Message as name/values list ... +msg2rec([MsgName | _], Dict) -> + Dict:msg2rec(MsgName); + +%% ... or record. +msg2rec(Rec, _) -> + element(1, Rec). + +%% values/2 + +%% Message as name/values list ... +values([_ | Avps], F, _) -> + if is_map(Avps) -> + maps:get(F, Avps, []); + is_list(Avps) -> + proplists:get_value(F, Avps, []) end; %% ... or record. -failed(Rec, FailedAvp, Dict) -> - try - RecName = element(1, Rec), - Dict:'#info-'(RecName, {index, 'Failed-AVP'}), - {'Failed-AVP', [FailedAvp]} - catch - error: _ -> - Avps = Dict:'#get-'('AVP', Rec), - A = #diameter_avp{name = 'Failed-AVP', - value = FailedAvp}, - {'AVP', [A|Avps]} - end. +values(Rec, F, Dict) -> + Dict:'#get-'(F, Rec). %% 3. Diameter Header %% @@ -1859,7 +1875,7 @@ str(T) -> get_avp(?RELAY, Name, Msg) -> get_avp(?BASE, Name, Msg); -%% Message as a header/avps list. +%% Message is a header/avps list. get_avp(Dict, Name, [#diameter_header{} | Avps]) -> try {Code, _, VId} = Dict:avp_header(Name), @@ -1872,16 +1888,16 @@ get_avp(Dict, Name, [#diameter_header{} | Avps]) -> undefined end; -%% Outgoing message as a name/values list. +%% 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; -%% Message is typically a record but not necessarily. +%% ... or record (but not necessarily). get_avp(Dict, Name, Rec) -> try #diameter_avp{name = Name, value = Dict:'#get-'(Name, Rec)} @@ -1890,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) -> @@ -1911,7 +1937,8 @@ ungroup(Avp) -> avp_decode(Dict, Name, #diameter_avp{value = undefined, data = Bin} - = Avp) -> + = Avp) + when is_binary(Bin) -> try Dict:avp(decode, Bin, Name, decode_opts(Dict)) of V -> Avp#diameter_avp{value = V} @@ -1933,7 +1960,8 @@ choose(false, _, X) -> X. %% Decode options sufficient for AVP extraction. decode_opts(Dict) -> - #{string_decode => false, + #{decode_format => record, + string_decode => false, strict_mbit => false, failed_avp => false, dictionary => Dict}. diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index a63425d92a..60baf1e8a4 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.erl @@ -72,7 +72,8 @@ restrict := boolean(), suspect := non_neg_integer(), %% OKAY -> SUSPECT okay := non_neg_integer()}, %% REOPEN -> OKAY - codec :: #{string_decode := false, + codec :: #{decode_format := false, + string_decode := false, strict_mbit := boolean(), failed_avp := false, rfc := 3588 | 6733, @@ -135,7 +136,8 @@ i({Ack, T, Pid, {Opts, putr(restart, {T, Opts, Svc, SvcOpts}), %% save seeing it in trace putr(dwr, dwr(Caps)), %% Nodes = restrict_nodes(Restrict), - CodecKeys = [string_decode, + CodecKeys = [decode_format, + string_decode, strict_mbit, incoming_maxlen, spawn_opt, @@ -155,7 +157,8 @@ i({Ack, T, Pid, {Opts, suspect => 1, okay => 3}, Opts)), - codec = maps:with(CodecKeys, SvcOpts#{string_decode := false, + codec = maps:with(CodecKeys, SvcOpts#{decode_format := false, + string_decode := false, ordered_encode => false})}. wait(Ref, Pid) -> |