diff options
Diffstat (limited to 'lib/diameter/src')
-rw-r--r-- | lib/diameter/src/base/diameter.erl | 2 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_capx.erl | 19 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_codec.erl | 20 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_config.erl | 9 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_peer_fsm.erl | 73 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_service.erl | 25 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_stats.erl | 5 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_traffic.erl | 14 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_types.erl | 89 | ||||
-rw-r--r-- | lib/diameter/src/base/diameter_watchdog.erl | 62 | ||||
-rw-r--r-- | lib/diameter/src/compiler/diameter_codegen.erl | 548 | ||||
-rw-r--r-- | lib/diameter/src/compiler/diameter_dict_util.erl | 42 | ||||
-rw-r--r-- | lib/diameter/src/compiler/diameter_make.erl | 249 | ||||
-rw-r--r-- | lib/diameter/src/diameter.appup.src | 84 | ||||
-rw-r--r-- | lib/diameter/src/transport/diameter_sctp.erl | 50 |
15 files changed, 709 insertions, 582 deletions
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl index 77200cc7d0..d74e091e11 100644 --- a/lib/diameter/src/base/diameter.erl +++ b/lib/diameter/src/base/diameter.erl @@ -343,7 +343,7 @@ call(SvcName, App, Message) -> | {capx_timeout, 'Unsigned32'()} | {disconnect_cb, evaluable()} | {length_errors, exit | handle | discard} - | {reconnect_timer, 'Unsigned32'()} + | {connect_timer, 'Unsigned32'()} | {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}} | {watchdog_config, [{okay|suspect, non_neg_integer()}]} | {spawn_opt, list()} diff --git a/lib/diameter/src/base/diameter_capx.erl b/lib/diameter/src/base/diameter_capx.erl index 1a931a9854..93548ecafd 100644 --- a/lib/diameter/src/base/diameter_capx.erl +++ b/lib/diameter/src/base/diameter_capx.erl @@ -168,12 +168,13 @@ ipaddr(A) -> %% %% Build a CER record to send to a remote peer. -%% Use the fact that diameter_caps has the same field names as CER. +%% Use the fact that diameter_caps is expected to have the same field +%% names as CER. bCER(#diameter_caps{} = Rec, Dict) -> - Values = lists:zip(Dict:'#info-'(diameter_base_CER, fields), + RecName = Dict:msg2rec('CER'), + Values = lists:zip(Dict:'#info-'(RecName, fields), tl(tuple_to_list(Rec))), - Dict:'#new-'(diameter_base_CER, [{K, map(K, V, Dict)} - || {K,V} <- Values]). + Dict:'#new-'(RecName, [{K, map(K, V, Dict)} || {K,V} <- Values]). %% map/3 %% @@ -186,8 +187,9 @@ bCER(#diameter_caps{} = Rec, Dict) -> %% since the corresponding dictionaries expect different values for a %% 'Vendor-Id': a list for 3588, an integer for 6733. -map('Vendor-Specific-Application-Id', L, Dict) -> - Rec = Dict:'#new-'('diameter_base_Vendor-Specific-Application-Id', []), +map('Vendor-Specific-Application-Id' = T, L, Dict) -> + RecName = Dict:name2rec(T), + Rec = Dict:'#new-'(RecName, []), Def = Dict:'#get-'('Vendor-Id', Rec), [vsa(V, Def) || V <- L]; map(_, V, _) -> @@ -342,8 +344,9 @@ cs(LS, RS) -> %% CER is a subset of CEA, the latter adding Result-Code and a few %% more AVP's. cea_from_cer(CER, Dict) -> - [diameter_base_CER | Values] = Dict:'#get-'(CER), - Dict:'#set-'(Values, Dict:'#new-'(diameter_base_CEA)). + RecName = Dict:msg2rec('CEA'), + [_ | Values] = Dict:'#get-'(CER), + Dict:'#set-'(Values, Dict:'#new-'(RecName)). %% rCEA/3 diff --git a/lib/diameter/src/base/diameter_codec.erl b/lib/diameter/src/base/diameter_codec.erl index 1d647b8c87..0de4d53973 100644 --- a/lib/diameter/src/base/diameter_codec.erl +++ b/lib/diameter/src/base/diameter_codec.erl @@ -477,8 +477,11 @@ split_head(<<Code:32, 1:1, M:1, P:1, _:5, Len:24, V:32, _/bitstring>>) -> split_head(<<Code:32, 0:1, M:1, P:1, _:5, Len:24, _/bitstring>>) -> {Code, undefined, M, P, Len, 8}; -split_head(Bin) -> - ?THROW({5014, #diameter_avp{data = Bin}}). +%% Header is truncated: pack_avp/1 will pad to the minimum header +%% length. +split_head(B) + when is_bitstring(B) -> + ?THROW({5014, #diameter_avp{data = B}}). %% 3588: %% @@ -523,9 +526,8 @@ split_data(_, _, _) -> %% split_data/4 split_data(Bin, HdrLen, Len, Pad) -> - <<_:HdrLen/binary, T/bitstring>> = Bin, - case T of - <<Data:Len/binary, _:Pad/binary, Rest/bitstring>> -> + case Bin of + <<_:HdrLen/binary, Data:Len/binary, _:Pad/binary, Rest/bitstring>> -> {Data, Rest}; _ -> invalid_avp_length() @@ -573,15 +575,15 @@ pack_avp(#diameter_avp{data = {Dict, Name, Value}} = A) -> {Name, Type} = Dict:avp_name(Code, Vid), pack_avp(A#diameter_avp{data = {Hdr, {Type, Value}}}); -pack_avp(#diameter_avp{code = undefined, data = Bin}) - when is_binary(Bin) -> +pack_avp(#diameter_avp{code = undefined, data = B}) + when is_bitstring(B) -> %% Reset the AVP Length of an AVP Header resulting from a 5014 %% error. The RFC doesn't explicitly say to do this but the %% receiver can't correctly extract this and following AVP's %% without a correct length. On the downside, the header doesn't %% reveal if the received header has been padded. - Pad = 8*header_length(Bin) - bit_size(Bin), - Len = size(<<H:5/binary, _:24, T/binary>> = <<Bin/bitstring, 0:Pad>>), + Pad = 8*header_length(B) - bit_size(B), + Len = size(<<H:5/binary, _:24, T/binary>> = <<B/bitstring, 0:Pad>>), <<H/binary, Len:24, T/binary>>; %% ... or as an iolist. diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl index fc5c284bf2..f5ea459fd0 100644 --- a/lib/diameter/src/base/diameter_config.erl +++ b/lib/diameter/src/base/diameter_config.erl @@ -537,7 +537,9 @@ opt({capx_timeout, Tmo}) -> opt({length_errors, T}) -> lists:member(T, [exit, handle, discard]); -opt({reconnect_timer, Tmo}) -> +opt({K, Tmo}) + when K == reconnect_timer; %% deprecated + K == connect_timer -> ?IS_UINT32(Tmo); opt({watchdog_timer, {M,F,A}}) @@ -651,8 +653,9 @@ make_opts(Opts, Defs) -> [{K, opt(K,V)} || {K,V} <- Known]. -opt(spawn_opt, L) -> - is_list(L); +opt(spawn_opt, L) + when is_list(L) -> + L; opt(K, false = B) when K /= sequence -> diff --git a/lib/diameter/src/base/diameter_peer_fsm.erl b/lib/diameter/src/base/diameter_peer_fsm.erl index 4e55864168..f76bd96c3c 100644 --- a/lib/diameter/src/base/diameter_peer_fsm.erl +++ b/lib/diameter/src/base/diameter_peer_fsm.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 @@ -28,7 +28,8 @@ -behaviour(gen_server). %% Interface towards diameter_watchdog. --export([start/3]). +-export([start/3, + result_code/2]). %% gen_server callbacks -export([init/1, @@ -62,7 +63,6 @@ %% Keys in process dictionary. -define(CB_KEY, cb). %% capabilities callback -define(DPR_KEY, dpr). %% disconnect callback --define(DWA_KEY, dwa). %% outgoing DWA -define(REF_KEY, ref). %% transport_ref() -define(Q_KEY, q). %% transport start queue -define(START_KEY, start). %% start of connected transport @@ -177,14 +177,9 @@ init(T) -> proc_lib:init_ack({ok, self()}), gen_server:enter_loop(?MODULE, [], i(T)). -i({Ack, WPid, {M, Ref} = T, Opts, {Mask, - Nodes, - Dict0, - #diameter_service{capabilities = LCaps} - = Svc}}) -> +i({Ack, WPid, {M, Ref} = T, Opts, {Mask, Nodes, Dict0, Svc}}) -> erlang:monitor(process, WPid), wait(Ack, WPid), - putr(?DWA_KEY, dwa(LCaps)), diameter_stats:reg(Ref), {[Cs,Ds], Rest} = proplists:split(Opts, [capabilities_cb, disconnect_cb]), putr(?CB_KEY, {Ref, [F || {_,F} <- Cs]}), @@ -194,11 +189,7 @@ i({Ack, WPid, {M, Ref} = T, Opts, {Mask, 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), @@ -612,9 +603,7 @@ rcv(Name, _, #state{state = PS}) Name == 'CEA' -> {stop, {Name, PS}}; -rcv(N, Pkt, S) - when N == 'DWR'; - N == 'DPR' -> +rcv('DPR' = N, Pkt, S) -> handle_request(N, Pkt, S); %% DPA in response to DPR and with the expected identifiers. @@ -717,8 +706,8 @@ build_answer(Type, errors = Es} = Pkt, S) -> - RC = rc(H, Es), - {answer(Type, RC, Es, S), post(Type, RC, Pkt, S)}. + {RC, FailedAVP} = result_code(H, Es), + {answer(Type, RC, FailedAVP, S), post(Type, RC, Pkt, S)}. inband_security([]) -> ?NO_INBAND_SECURITY; @@ -734,7 +723,7 @@ cea(CEA, RC, Dict0) -> post('CER' = T, RC, Pkt, S) -> {T, caps(S), {RC, Pkt}}; -post(_, _, _, _) -> +post('DPR', _, _, _) -> ok. rejected({capabilities_cb, _F, Reason}, T, S) -> @@ -743,20 +732,20 @@ rejected({capabilities_cb, _F, Reason}, T, S) -> rejected(discard, T, _) -> close(T); rejected({N, Es}, T, S) -> - {answer('CER', N, Es, S), T}; + {answer('CER', N, failed_avp(N, Es), S), T}; rejected(N, T, S) -> rejected({N, []}, T, S). -answer(Type, RC, Es, S) -> - set(answer(Type, RC, S), failed_avp(RC, Es)). - failed_avp(RC, [{RC, Avp} | _]) -> - [{'Failed-AVP', [{'AVP', [Avp]}]}]; + [{'Failed-AVP', [[{'AVP', [Avp]}]]}]; failed_avp(RC, [_ | Es]) -> failed_avp(RC, Es); failed_avp(_, [] = No) -> No. +answer(Type, RC, FailedAVP, S) -> + set(answer(Type, RC, S), FailedAVP). + answer(Type, RC, S) -> answer_message(answer(Type, S), RC). @@ -784,29 +773,25 @@ set(['answer-message' | _] = Ans, FailedAvp) -> set([_|_] = Ans, FailedAvp) -> Ans ++ FailedAvp. -%% rc/2 - -rc(#diameter_header{is_error = true}, _) -> - 3008; %% DIAMETER_INVALID_HDR_BITS +%% result_code/2 -rc(_, [Bs|_]) - when is_bitstring(Bs) -> %% from old code - 3009; %% DIAMETER_INVALID_HDR_BITS +result_code(#diameter_header{is_error = true}, _) -> + {3008, []}; %% DIAMETER_INVALID_HDR_BITS -rc(#diameter_header{version = ?DIAMETER_VERSION}, Es) -> +result_code(#diameter_header{version = ?DIAMETER_VERSION}, Es) -> rc(Es); -rc(_, _) -> - 5011. %% DIAMETER_UNSUPPORTED_VERSION +result_code(_, _) -> + {5011, []}. %% DIAMETER_UNSUPPORTED_VERSION %% rc/1 rc([]) -> - 2001; %% DIAMETER_SUCCESS -rc([{RC,_}|_]) -> - RC; + {2001, []}; %% DIAMETER_SUCCESS +rc([{RC, _} | _] = Es) -> + {RC, failed_avp(RC, Es)}; rc([RC|_]) -> - RC. + {RC, []}. %% DIAMETER_INVALID_HDR_BITS 3008 %% A request was received whose bits in the Diameter header were @@ -832,9 +817,6 @@ rc([RC|_]) -> %% answer/2 -answer('DWR', _) -> - getr(?DWA_KEY); - answer(Name, #state{service = #diameter_service{capabilities = Caps}}) -> a(Name, Caps). @@ -1019,15 +1001,6 @@ report({M, _, _, _, _} = T) report(_) -> ok. -%% dwa/1 - -dwa(#diameter_caps{origin_host = OH, - origin_realm = OR, - origin_state_id = OSI}) -> - ['DWA', {'Origin-Host', OH}, - {'Origin-Realm', OR}, - {'Origin-State-Id', OSI}]. - %% dpr/2 %% %% The RFC isn't clear on whether DPR should be send in a non-Open diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl index 9dd8aafc61..1274e0fc48 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 @@ -673,7 +673,8 @@ service_options(Opts) -> {use_shared_peers, get_value(use_shared_peers, Opts)}, {restrict_connections, proplists:get_value(restrict_connections, Opts, - ?RESTRICT)}]. + ?RESTRICT)}, + {spawn_opt, proplists:get_value(spawn_opt, Opts, [])}]. %% The order of options is significant since we match against the list. mref(false = No) -> @@ -700,8 +701,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) -> []; @@ -1142,10 +1142,17 @@ q_restart(false, _) -> %% communicate. default_tc(connect, Opts) -> - proplists:get_value(reconnect_timer, Opts, ?DEFAULT_TC); + connect_timer(Opts, ?DEFAULT_TC); default_tc(accept, _) -> 0. +%% Accept both connect_timer and the (older) reconnect_timer, the +%% latter being a remnant from a time in which the timer did apply to +%% reconnect attempts. +connect_timer(Opts, Def0) -> + Def = proplists:get_value(reconnect_timer, Opts, Def0), + proplists:get_value(connect_timer, Opts, Def). + %% Bound tc below if the watchdog was restarted recently to avoid %% continuous restarted in case of faulty config or other problems. tc(Time, Tc) -> @@ -1180,7 +1187,7 @@ 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 reconnect_timer timeout. +%% connected peer, or us after connect_timer timeout. close(#watchdog{type = connect}, _) -> ok; @@ -1193,16 +1200,16 @@ close(#watchdog{type = accept, %% Tell watchdog to (maybe) die later ... c(Pid, true, Opts) -> - Tc = proplists:get_value(reconnect_timer, Opts, 2*?DEFAULT_TC), + Tc = connect_timer(Opts, 2*?DEFAULT_TC), erlang:send_after(Tc, Pid, close); %% ... or now. c(Pid, false, _Opts) -> Pid ! close. -%% The RFC's only document the behaviour of Tc, our reconnect_timer, +%% The RFC's only document the behaviour of Tc, our connect_timer, %% for the establishment of connections but we also give -%% reconnect_timer semantics for a listener, being the time within +%% connect_timer semantics for a listener, being the time within %% which a new connection attempt is expected of a connecting peer. %% The value should be greater than the peer's Tc + jitter. 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 8b6f026b34..7fbb306b02 100644 --- a/lib/diameter/src/base/diameter_traffic.erl +++ b/lib/diameter/src/base/diameter_traffic.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013. All Rights Reserved. +%% Copyright Ericsson AB 2013-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 @@ -90,9 +90,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 @@ -305,15 +302,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_types.erl b/lib/diameter/src/base/diameter_types.erl index 9ae289034c..ca3338be5f 100644 --- a/lib/diameter/src/base/diameter_types.erl +++ b/lib/diameter/src/base/diameter_types.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2013. 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 @@ -92,6 +92,9 @@ when is_binary(Bin) -> binary_to_list(Bin); +'OctetString'(decode, B) -> + ?INVALID_LENGTH(B); + 'OctetString'(encode = M, zero) -> 'OctetString'(M, []); @@ -250,44 +253,19 @@ 'Address'(encode, zero) -> <<0:48>>; -'Address'(decode, <<1:16, B/binary>>) - when size(B) == 4 -> - list_to_tuple(binary_to_list(B)); - -'Address'(decode, <<2:16, B/binary>>) - when size(B) == 16 -> - list_to_tuple(v6dec(B, [])); +'Address'(decode, <<A:16, B/binary>>) + when 1 == A, 4 == size(B); + 2 == A, 16 == size(B) -> + list_to_tuple([N || <<N:A/unit:8>> <= B]); -'Address'(decode, <<A:16, _/binary>> = B) - when 1 == A; - 2 == A -> +'Address'(decode, B) -> ?INVALID_LENGTH(B); 'Address'(encode, T) -> - ipenc(diameter_lib:ipaddr(T)). - -ipenc(T) - when is_tuple(T), size(T) == 4 -> - B = list_to_binary(tuple_to_list(T)), - <<1:16, B/binary>>; - -ipenc(T) - when is_tuple(T), size(T) == 8 -> - B = v6enc(lists:reverse(tuple_to_list(T)), <<>>), - <<2:16, B/binary>>. - -v6dec(<<N:16, B/binary>>, Acc) -> - v6dec(B, [N | Acc]); - -v6dec(<<>>, Acc) -> - lists:reverse(Acc). - -v6enc([N | Rest], B) - when ?UINT(16,N) -> - v6enc(Rest, <<N:16, B/binary>>); - -v6enc([], B) -> - B. + Ns = tuple_to_list(diameter_lib:ipaddr(T)), %% length 4 or 8 + A = length(Ns) div 4, %% 1 or 2 + B = << <<N:A/unit:8>> || N <- Ns >>, + <<A:16, B/binary>>. %% -------------------- @@ -301,7 +279,10 @@ v6enc([], B) -> <<_,_/binary>> = 'OctetString'(M, X); 'DiameterIdentity'(decode = M, <<_,_/binary>> = X) -> - 'OctetString'(M, X). + 'OctetString'(M, X); + +'DiameterIdentity'(decode, X) -> + ?INVALID_LENGTH(X). %% -------------------- @@ -309,6 +290,9 @@ v6enc([], B) -> when is_binary(Bin) -> scan_uri(Bin); +'DiameterURI'(decode, B) -> + ?INVALID_LENGTH(B); + %% The minimal DiameterURI is "aaa://x", 7 characters. 'DiameterURI'(encode = M, zero) -> 'OctetString'(M, lists:duplicate(0,7)); @@ -353,37 +337,18 @@ v6enc([], B) -> %% -------------------- -'UTF8String'(decode, Bin) -> - udec(Bin, []); +'UTF8String'(decode, Bin) + when is_binary(Bin) -> + tl([0|_] = unicode:characters_to_list([0, Bin])); %% assert list return + +'UTF8String'(decode, B) -> + ?INVALID_LENGTH(B); 'UTF8String'(encode = M, zero) -> 'UTF8String'(M, []); 'UTF8String'(encode, S) -> - uenc(S, []). - -udec(<<>>, Acc) -> - lists:reverse(Acc); - -udec(<<C/utf8, Rest/binary>>, Acc) -> - udec(Rest, [C | Acc]). - -uenc(E, Acc) - when E == []; - E == <<>> -> - list_to_binary(lists:reverse(Acc)); - -uenc(<<C/utf8, Rest/binary>>, Acc) -> - uenc(Rest, [<<C/utf8>> | Acc]); - -uenc([[] | Rest], Acc) -> - uenc(Rest, Acc); - -uenc([[H|T] | Rest], Acc) -> - uenc([H, T | Rest], Acc); - -uenc([C | Rest], Acc) -> - uenc(Rest, [<<C/utf8>> | Acc]). + <<_/binary>> = unicode:characters_to_binary(S). %% assert binary return %% -------------------- diff --git a/lib/diameter/src/base/diameter_watchdog.erl b/lib/diameter/src/base/diameter_watchdog.erl index 88ccf630e2..53e659e3f6 100644 --- a/lib/diameter/src/base/diameter_watchdog.erl +++ b/lib/diameter/src/base/diameter_watchdog.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 @@ -157,8 +157,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 +165,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 @@ -201,7 +197,7 @@ common_dictionary(Apps) -> %% means a user won't be able either send of receive %% messages in the common dictionary: incoming request %% will be answered with 3007 and outgoing requests cannot - %% be sent. The dictionary returned here is oly used for + %% be sent. The dictionary returned here is only used for %% messages diameter sends and receives: CER/CEA, DPR/DPA %% and DWR/DWA. ?BASE @@ -252,17 +248,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}}, @@ -329,7 +314,7 @@ code_change(_, State, _) -> %% the commentary is ours. %% Service or watchdog is telling the watchdog of an accepting -%% transport to die after reconnect_timer expiry or reestablished +%% transport to die after connect_timer expiry or reestablished %% connection (in another transport process) respectively. transition(close, #watchdog{status = down}) -> {{accept, _}, _, _} = getr(restart), %% assert @@ -461,15 +446,28 @@ eraser(Key) -> %% encode/3 -encode(Msg, Mask, Dict) -> +encode(dwr = M, Dict0, Mask) -> + Msg = getr(M), Seq = diameter_session:sequence(Mask), Hdr = #diameter_header{version = ?DIAMETER_VERSION, end_to_end_id = Seq, hop_by_hop_id = Seq}, Pkt = #diameter_packet{header = Hdr, msg = Msg}, - #diameter_packet{bin = Bin} = diameter_codec:encode(Dict, Pkt), - Bin. + #diameter_packet{bin = Bin} = diameter_codec:encode(Dict0, Pkt), + Bin; + + +encode(dwa, Dict0, #diameter_packet{header = H, transport_data = TD} + = ReqPkt) -> + AnsPkt = #diameter_packet{header + = H#diameter_header{is_request = false, + is_error = undefined, + is_retransmitted = false}, + msg = dwa(ReqPkt), + transport_data = TD}, + + diameter_codec:encode(Dict0, AnsPkt). %% okay/3 @@ -527,7 +525,7 @@ send_watchdog(#watchdog{pending = false, dictionary = Dict0, sequence = Mask} = S) -> - send(TPid, {send, encode(getr(dwr), Mask, Dict0)}), + send(TPid, {send, encode(dwr, Dict0, Mask)}), ?LOG(send, 'DWR'), S#watchdog{pending = true}. @@ -545,10 +543,14 @@ recv(Name, Pkt, S) -> %% rcv/3 +rcv('DWR', Pkt, #watchdog{transport = TPid, + dictionary = Dict0}) -> + send(TPid, {send, encode(dwa, Dict0, Pkt)}), + ?LOG(send, 'DWA'); + rcv(N, _, _) when N == 'CER'; N == 'CEA'; - N == 'DWR'; N == 'DWA'; N == 'DPR'; N == 'DPA' -> @@ -642,6 +644,9 @@ rcv('DWA', #watchdog{status = reopen, %% REOPEN Receive non-DWA Throwaway() REOPEN +rcv('DWR', #watchdog{status = reopen} = S) -> + S; %% ensure DWA: the RFC isn't explicit about answering + rcv(_, #watchdog{status = reopen} = S) -> throwaway(S). @@ -782,6 +787,13 @@ dwr(#diameter_caps{origin_host = OH, {'Origin-Realm', OR}, {'Origin-State-Id', OSI}]. +%% dwa/1 + +dwa(#diameter_packet{header = H, errors = Es}) -> + {RC, FailedAVP} = diameter_peer_fsm:result_code(H, Es), + ['DWA', {'Result-Code', RC} + | tl(getr(dwr)) ++ FailedAVP]. + %% restrict_nodes/1 restrict_nodes(false) -> diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index e687145263..22422f2ef2 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -33,11 +33,6 @@ -export([from_dict/4]). -%% Internal exports (for test). --export([file/1, - file/2, - file/3]). - -include("diameter_forms.hrl"). -include("diameter_vsn.hrl"). @@ -48,18 +43,61 @@ %% =========================================================================== --spec from_dict(File, Spec, Opts, Mode) +-spec from_dict(File, ParseD, Opts, Mode) -> ok + | term() when File :: string(), - Spec :: orddict:orddict(), + ParseD :: orddict:orddict(), Opts :: list(), - Mode :: spec | erl | hrl. + Mode :: parse | forms | erl | hrl. -from_dict(File, Spec, Opts, Mode) -> +from_dict(File, ParseD, Opts, Mode) -> Outdir = proplists:get_value(outdir, Opts, "."), + Return = proplists:get_value(return, Opts, false), + Mod = mod(File, orddict:find(name, ParseD)), putr(verbose, lists:member(verbose, Opts)), - putr(debug, lists:member(debug, Opts)), - codegen(File, Spec, Outdir, Mode). + try + maybe_write(Return, Mode, Outdir, Mod, gen(Mode, ParseD, ?A(Mod))) + after + eraser(verbose) + end. + +mod(File, error) -> + filename:rootname(filename:basename(File)); +mod(_, {ok, Mod}) -> + Mod. + +maybe_write(true, _, _, _, T) -> + T; + +maybe_write(_, Mode, Outdir, Mod, T) -> + Path = filename:join(Outdir, Mod), %% minus extension + do_write(Mode, [Path, $., ext(Mode)], T). + +ext(parse) -> + "D"; +ext(forms) -> + "F"; +ext(T) -> + ?S(T). + +do_write(M, Path, T) + when M == parse; + M == forms -> + write_term(Path, T); +do_write(_, Path, T) -> + write(Path, T). + +write(Path, T) -> + write(Path, "~s", T). + +write_term(Path, T) -> + write(Path, "~p.~n", T). + +write(Path, Fmt, T) -> + {ok, Fd} = file:open(Path, [write]), + io:fwrite(Fd, Fmt, [T]), + ok = file:close(Fd). %% Optional reports when running verbosely. report(What, Data) -> @@ -77,20 +115,8 @@ putr(Key, Value) -> getr(Key) -> get({?MODULE, Key}). -%% =========================================================================== -%% =========================================================================== - -%% Generate from parsed dictionary in a file. - -file(F) -> - file(F, spec). - -file(F, Mode) -> - file(F, ".", Mode). - -file(F, Outdir, Mode) -> - {ok, [Spec]} = file:consult(F), - from_dict(F, Spec, Outdir, Mode). +eraser(Key) -> + erase({?MODULE, Key}). %% =========================================================================== %% =========================================================================== @@ -98,97 +124,68 @@ file(F, Outdir, Mode) -> get_value(Key, Plist) -> proplists:get_value(Key, Plist, []). -write(Path, Str) -> - w(Path, Str, "~s"). - -write_term(Path, T) -> - w(Path, T, "~p."). - -w(Path, T, Fmt) -> - {ok, Fd} = file:open(Path, [write]), - io:fwrite(Fd, Fmt ++ "~n", [T]), - file:close(Fd). - -codegen(File, Spec, Outdir, Mode) -> - Mod = mod(File, orddict:find(name, Spec)), - Path = filename:join(Outdir, Mod), %% minus extension - gen(Mode, Spec, ?A(Mod), Path), - ok. - -mod(File, error) -> - filename:rootname(filename:basename(File)); -mod(_, {ok, Mod}) -> - Mod. - -gen(spec, Spec, _Mod, Path) -> - write_term(Path ++ ".spec", [?VERSION | Spec]); - -gen(hrl, Spec, Mod, Path) -> - gen_hrl(Path ++ ".hrl", Mod, Spec); - -gen(erl, Spec, Mod, Path) -> - Forms = [{?attribute, module, Mod}, - {?attribute, compile, {parse_transform, diameter_exprecs}}, - {?attribute, compile, nowarn_unused_function}, - {?attribute, export, [{name, 0}, - {id, 0}, - {vendor_id, 0}, - {vendor_name, 0}, - {decode_avps, 2}, %% in diameter_gen.hrl - {encode_avps, 2}, %% - {msg_name, 2}, - {msg_header, 1}, - {rec2msg, 1}, - {msg2rec, 1}, - {name2rec, 1}, - {avp_name, 2}, - {avp_arity, 2}, - {avp_header, 1}, - {avp, 3}, - {grouped_avp, 3}, - {enumerated_avp, 3}, - {empty_value, 1}, - {dict, 0}]}, - %% diameter.hrl is included for #diameter_avp - {?attribute, include_lib, "diameter/include/diameter.hrl"}, - {?attribute, include_lib, "diameter/include/diameter_gen.hrl"}, - f_name(Mod), - f_id(Spec), - f_vendor_id(Spec), - f_vendor_name(Spec), - f_msg_name(Spec), - f_msg_header(Spec), - f_rec2msg(Spec), - f_msg2rec(Spec), - f_name2rec(Spec), - f_avp_name(Spec), - f_avp_arity(Spec), - f_avp_header(Spec), - f_avp(Spec), - f_enumerated_avp(Spec), - f_empty_value(Spec), - f_dict(Spec), - {eof, ?LINE}], - - gen_erl(Path, insert_hrl_forms(Spec, Forms)). - -gen_erl(Path, Forms) -> - getr(debug) andalso write_term(Path ++ ".forms", Forms), - write(Path ++ ".erl", - header() ++ erl_prettypr:format(erl_syntax:form_list(Forms))). - -insert_hrl_forms(Spec, Forms) -> - {H,T} = lists:splitwith(fun is_header/1, Forms), - H ++ make_hrl_forms(Spec) ++ T. - -is_header({attribute, _, export, _}) -> - false; -is_header(_) -> - true. - -make_hrl_forms(Spec) -> +gen(parse, ParseD, _Mod) -> + [?VERSION | ParseD]; + +gen(forms, ParseD, Mod) -> + pp(erl_forms(Mod, ParseD)); + +gen(hrl, ParseD, Mod) -> + gen_hrl(Mod, ParseD); + +gen(erl, ParseD, Mod) -> + [header(), prettypr(erl_forms(Mod, ParseD)), $\n]. + +erl_forms(Mod, ParseD) -> + Forms = [[{?attribute, module, Mod}, + {?attribute, compile, {parse_transform, diameter_exprecs}}, + {?attribute, compile, nowarn_unused_function}], + make_hrl_forms(ParseD), + [{?attribute, export, [{name, 0}, + {id, 0}, + {vendor_id, 0}, + {vendor_name, 0}, + {decode_avps, 2}, %% in diameter_gen.hrl + {encode_avps, 2}, %% + {msg_name, 2}, + {msg_header, 1}, + {rec2msg, 1}, + {msg2rec, 1}, + {name2rec, 1}, + {avp_name, 2}, + {avp_arity, 2}, + {avp_header, 1}, + {avp, 3}, + {grouped_avp, 3}, + {enumerated_avp, 3}, + {empty_value, 1}, + {dict, 0}]}, + %% diameter.hrl is included for #diameter_avp + {?attribute, include_lib, "diameter/include/diameter.hrl"}, + {?attribute, include_lib, "diameter/include/diameter_gen.hrl"}, + f_name(Mod), + f_id(ParseD), + f_vendor_id(ParseD), + f_vendor_name(ParseD), + f_msg_name(ParseD), + f_msg_header(ParseD), + f_rec2msg(ParseD), + f_msg2rec(ParseD), + f_name2rec(ParseD), + f_avp_name(ParseD), + f_avp_arity(ParseD), + f_avp_header(ParseD), + f_avp(ParseD), + f_enumerated_avp(ParseD), + f_empty_value(ParseD), + f_dict(ParseD), + {eof, ?LINE}]], + + lists:append(Forms). + +make_hrl_forms(ParseD) -> {_Prefix, MsgRecs, GrpRecs, ImportedGrpRecs} - = make_record_forms(Spec), + = make_record_forms(ParseD), RecordForms = MsgRecs ++ GrpRecs ++ lists:flatmap(fun({_,Fs}) -> Fs end, ImportedGrpRecs), @@ -199,16 +196,16 @@ make_hrl_forms(Spec) -> %% export_records is used by the diameter_exprecs parse transform. [{?attribute, export_records, RecNames} | RecordForms]. -make_record_forms(Spec) -> - Prefix = prefix(Spec), +make_record_forms(ParseD) -> + Prefix = prefix(ParseD), - MsgRecs = a_record(Prefix, fun msg_proj/1, get_value(messages, Spec)), - GrpRecs = a_record(Prefix, fun grp_proj/1, get_value(grouped, Spec)), + MsgRecs = a_record(Prefix, fun msg_proj/1, get_value(messages, ParseD)), + GrpRecs = a_record(Prefix, fun grp_proj/1, get_value(grouped, ParseD)), ImportedGrpRecs = [{M, a_record(Prefix, fun grp_proj/1, Gs)} - || {M,Gs} <- get_value(import_groups, Spec)], + || {M,Gs} <- get_value(import_groups, ParseD)], - {Prefix, MsgRecs, GrpRecs, ImportedGrpRecs}. + {to_upper(Prefix), MsgRecs, GrpRecs, ImportedGrpRecs}. msg_proj({Name, _, _, _, Avps}) -> {Name, Avps}. @@ -246,9 +243,9 @@ f_name(Name) -> %%% # id/0 %%% ------------------------------------------------------------------------ -f_id(Spec) -> +f_id(ParseD) -> {?function, id, 0, - [c_id(orddict:find(id, Spec))]}. + [c_id(orddict:find(id, ParseD))]}. c_id({ok, Id}) -> {?clause, [], [], [?INTEGER(Id)]}; @@ -260,9 +257,9 @@ c_id(error) -> %%% # vendor_id/0 %%% ------------------------------------------------------------------------ -f_vendor_id(Spec) -> +f_vendor_id(ParseD) -> {?function, vendor_id, 0, - [{?clause, [], [], [b_vendor_id(orddict:find(vendor, Spec))]}]}. + [{?clause, [], [], [b_vendor_id(orddict:find(vendor, ParseD))]}]}. b_vendor_id({ok, {Id, _}}) -> ?INTEGER(Id); @@ -273,9 +270,9 @@ b_vendor_id(error) -> %%% # vendor_name/0 %%% ------------------------------------------------------------------------ -f_vendor_name(Spec) -> +f_vendor_name(ParseD) -> {?function, vendor_name, 0, - [{?clause, [], [], [b_vendor_name(orddict:find(vendor, Spec))]}]}. + [{?clause, [], [], [b_vendor_name(orddict:find(vendor, ParseD))]}]}. b_vendor_name({ok, {_, Name}}) -> ?Atom(Name); @@ -286,15 +283,15 @@ b_vendor_name(error) -> %%% # msg_name/1 %%% ------------------------------------------------------------------------ -f_msg_name(Spec) -> - {?function, msg_name, 2, msg_name(Spec)}. +f_msg_name(ParseD) -> + {?function, msg_name, 2, msg_name(ParseD)}. %% Return the empty name for any unknown command to which %% DIAMETER_COMMAND_UNSUPPORTED should be replied. -msg_name(Spec) -> +msg_name(ParseD) -> lists:flatmap(fun c_msg_name/1, proplists:get_value(command_codes, - Spec, + ParseD, [])) ++ [{?clause, [?VAR('_'), ?VAR('_')], [], [?ATOM('')]}]. @@ -310,12 +307,12 @@ c_msg_name({Code, Req, Ans}) -> %%% # msg2rec/1 %%% ------------------------------------------------------------------------ -f_msg2rec(Spec) -> - {?function, msg2rec, 1, msg2rec(Spec)}. +f_msg2rec(ParseD) -> + {?function, msg2rec, 1, msg2rec(ParseD)}. -msg2rec(Spec) -> - Pre = prefix(Spec), - lists:map(fun(T) -> c_msg2rec(T, Pre) end, get_value(messages, Spec)) +msg2rec(ParseD) -> + Pre = prefix(ParseD), + lists:map(fun(T) -> c_msg2rec(T, Pre) end, get_value(messages, ParseD)) ++ [?BADARG(1)]. c_msg2rec({N,_,_,_,_}, Pre) -> @@ -325,12 +322,12 @@ c_msg2rec({N,_,_,_,_}, Pre) -> %%% # rec2msg/1 %%% ------------------------------------------------------------------------ -f_rec2msg(Spec) -> - {?function, rec2msg, 1, rec2msg(Spec)}. +f_rec2msg(ParseD) -> + {?function, rec2msg, 1, rec2msg(ParseD)}. -rec2msg(Spec) -> - Pre = prefix(Spec), - lists:map(fun(T) -> c_rec2msg(T, Pre) end, get_value(messages, Spec)) +rec2msg(ParseD) -> + Pre = prefix(ParseD), + lists:map(fun(T) -> c_rec2msg(T, Pre) end, get_value(messages, ParseD)) ++ [?BADARG(1)]. c_rec2msg({N,_,_,_,_}, Pre) -> @@ -340,13 +337,13 @@ c_rec2msg({N,_,_,_,_}, Pre) -> %%% # name2rec/1 %%% ------------------------------------------------------------------------ -f_name2rec(Spec) -> - {?function, name2rec, 1, name2rec(Spec)}. +f_name2rec(ParseD) -> + {?function, name2rec, 1, name2rec(ParseD)}. -name2rec(Spec) -> - Pre = prefix(Spec), - Groups = get_value(grouped, Spec) - ++ lists:flatmap(fun avps/1, get_value(import_groups, Spec)), +name2rec(ParseD) -> + Pre = prefix(ParseD), + Groups = get_value(grouped, ParseD) + ++ lists:flatmap(fun avps/1, get_value(import_groups, ParseD)), lists:map(fun({N,_,_,_}) -> c_name2rec(N, Pre) end, Groups) ++ [{?clause, [?VAR('T')], [], [?CALL(msg2rec, [?VAR('T')])]}]. @@ -360,8 +357,8 @@ avps({_Mod, Avps}) -> %%% # avp_name/1 %%% ------------------------------------------------------------------------ -f_avp_name(Spec) -> - {?function, avp_name, 2, avp_name(Spec)}. +f_avp_name(ParseD) -> + {?function, avp_name, 2, avp_name(ParseD)}. %% 3588, 4.1: %% @@ -372,11 +369,11 @@ f_avp_name(Spec) -> %% field. AVP numbers 256 and above are used for Diameter, which are %% allocated by IANA (see Section 11.1). -avp_name(Spec) -> - Avps = get_value(avp_types, Spec), - Imported = get_value(import_avps, Spec), - Vid = orddict:find(vendor, Spec), - Vs = vendor_id_map(Spec), +avp_name(ParseD) -> + Avps = get_value(avp_types, ParseD), + Imported = get_value(import_avps, ParseD), + Vid = orddict:find(vendor, ParseD), + Vs = vendor_id_map(ParseD), lists:map(fun(T) -> c_avp_name(T, Vs, Vid) end, Avps) ++ lists:flatmap(fun(T) -> c_imported_avp_name(T, Vs) end, Imported) @@ -407,25 +404,25 @@ c_avp_name_(T, Code, Vid) -> [], [T]}. -vendor_id_map(Spec) -> +vendor_id_map(ParseD) -> lists:flatmap(fun({V,Ns}) -> [{N,V} || N <- Ns] end, - get_value(avp_vendor_id, Spec)) + get_value(avp_vendor_id, ParseD)) ++ lists:flatmap(fun({_,_,[],_}) -> []; ({N,_,[V],_}) -> [{N,V}] end, - get_value(grouped, Spec)). + get_value(grouped, ParseD)). %%% ------------------------------------------------------------------------ %%% # avp_arity/2 %%% ------------------------------------------------------------------------ -f_avp_arity(Spec) -> - {?function, avp_arity, 2, avp_arity(Spec)}. +f_avp_arity(ParseD) -> + {?function, avp_arity, 2, avp_arity(ParseD)}. -avp_arity(Spec) -> - Msgs = get_value(messages, Spec), - Groups = get_value(grouped, Spec) - ++ lists:flatmap(fun avps/1, get_value(import_groups, Spec)), +avp_arity(ParseD) -> + Msgs = get_value(messages, ParseD), + Groups = get_value(grouped, ParseD) + ++ lists:flatmap(fun avps/1, get_value(import_groups, ParseD)), c_avp_arity(Msgs ++ Groups) ++ [{?clause, [?VAR('_'), ?VAR('_')], [], [?INTEGER(0)]}]. @@ -449,15 +446,15 @@ c_arity(Name, Avp) -> %%% # avp/3 %%% ------------------------------------------------------------------------ -f_avp(Spec) -> - {?function, avp, 3, avp(Spec) ++ [?BADARG(3)]}. +f_avp(ParseD) -> + {?function, avp, 3, avp(ParseD) ++ [?BADARG(3)]}. -avp(Spec) -> - Native = get_value(avp_types, Spec), - CustomMods = get_value(custom_types, Spec), - TypeMods = get_value(codecs, Spec), - Imported = get_value(import_avps, Spec), - Enums = get_value(enum, Spec), +avp(ParseD) -> + Native = get_value(avp_types, ParseD), + CustomMods = get_value(custom_types, ParseD), + TypeMods = get_value(codecs, ParseD), + Imported = get_value(import_avps, ParseD), + Enums = get_value(enum, ParseD), Custom = lists:map(fun({M,As}) -> {M, custom_types, As} end, CustomMods) @@ -548,14 +545,14 @@ custom(codecs, AvpName, Type) -> %%% # enumerated_avp/3 %%% ------------------------------------------------------------------------ -f_enumerated_avp(Spec) -> - {?function, enumerated_avp, 3, enumerated_avp(Spec) ++ [?BADARG(3)]}. +f_enumerated_avp(ParseD) -> + {?function, enumerated_avp, 3, enumerated_avp(ParseD) ++ [?BADARG(3)]}. -enumerated_avp(Spec) -> - Enums = get_value(enum, Spec), +enumerated_avp(ParseD) -> + Enums = get_value(enum, ParseD), lists:flatmap(fun cs_enumerated_avp/1, Enums) ++ lists:flatmap(fun({M,Es}) -> enumerated_avp(M, Es, Enums) end, - get_value(import_enums, Spec)). + get_value(import_enums, ParseD)). enumerated_avp(Mod, Es, Enums) -> lists:flatmap(fun({N,_}) -> @@ -585,16 +582,16 @@ c_enumerated_avp(AvpName, {_,I}) -> %%% msg_header/1 %%% ------------------------------------------------------------------------ -f_msg_header(Spec) -> - {?function, msg_header, 1, msg_header(Spec) ++ [?BADARG(1)]}. +f_msg_header(ParseD) -> + {?function, msg_header, 1, msg_header(ParseD) ++ [?BADARG(1)]}. -msg_header(Spec) -> - msg_header(get_value(messages, Spec), Spec). +msg_header(ParseD) -> + msg_header(get_value(messages, ParseD), ParseD). msg_header([], _) -> []; -msg_header(Msgs, Spec) -> - ApplId = orddict:fetch(id, Spec), +msg_header(Msgs, ParseD) -> + ApplId = orddict:fetch(id, ParseD), lists:map(fun({M,C,F,_,_}) -> c_msg_header(M, C, F, ApplId) end, Msgs). @@ -616,14 +613,14 @@ emf('ERR', N) -> N bor 2#00100000. %%% # avp_header/1 %%% ------------------------------------------------------------------------ -f_avp_header(Spec) -> - {?function, avp_header, 1, avp_header(Spec) ++ [?BADARG(1)]}. +f_avp_header(ParseD) -> + {?function, avp_header, 1, avp_header(ParseD) ++ [?BADARG(1)]}. -avp_header(Spec) -> - Native = get_value(avp_types, Spec), - Imported = get_value(import_avps, Spec), - Vid = orddict:find(vendor, Spec), - Vs = vendor_id_map(Spec), +avp_header(ParseD) -> + Native = get_value(avp_types, ParseD), + Imported = get_value(import_avps, ParseD), + Vid = orddict:find(vendor, ParseD), + Vs = vendor_id_map(ParseD), lists:flatmap(fun(A) -> c_avp_header(A, Vs, Vid) end, Native ++ Imported). @@ -679,14 +676,14 @@ v(false, _, _, _) -> %%% # empty_value/0 %%% ------------------------------------------------------------------------ -f_empty_value(Spec) -> - {?function, empty_value, 1, empty_value(Spec)}. +f_empty_value(ParseD) -> + {?function, empty_value, 1, empty_value(ParseD)}. -empty_value(Spec) -> - Imported = lists:flatmap(fun avps/1, get_value(import_enums, Spec)), - Groups = get_value(grouped, Spec) - ++ lists:flatmap(fun avps/1, get_value(import_groups, Spec)), - Enums = [T || {N,_} = T <- get_value(enum, Spec), +empty_value(ParseD) -> + Imported = lists:flatmap(fun avps/1, get_value(import_enums, ParseD)), + Groups = get_value(grouped, ParseD) + ++ lists:flatmap(fun avps/1, get_value(import_groups, ParseD)), + Enums = [T || {N,_} = T <- get_value(enum, ParseD), not lists:keymember(N, 1, Imported)] ++ Imported, lists:map(fun c_empty_value/1, Groups ++ Enums) @@ -706,72 +703,52 @@ c_empty_value({Name, _}) -> %%% # dict/0 %%% ------------------------------------------------------------------------ -f_dict(Spec) -> +f_dict(ParseD) -> {?function, dict, 0, - [{?clause, [], [], [?TERM([?VERSION | Spec])]}]}. + [{?clause, [], [], [?TERM([?VERSION | ParseD])]}]}. %%% ------------------------------------------------------------------------ -%%% # gen_hrl/3 +%%% # gen_hrl/2 %%% ------------------------------------------------------------------------ -gen_hrl(Path, Mod, Spec) -> - {ok, Fd} = file:open(Path, [write]), - +gen_hrl(Mod, ParseD) -> {Prefix, MsgRecs, GrpRecs, ImportedGrpRecs} - = make_record_forms(Spec), - - file:write(Fd, hrl_header(Mod)), - - forms("Message records", Fd, MsgRecs), - forms("Grouped AVP records", Fd, GrpRecs), - - lists:foreach(fun({M,Fs}) -> - forms("Grouped AVP records from " ++ atom_to_list(M), - Fd, - Fs) - end, - ImportedGrpRecs), - - PREFIX = to_upper(Prefix), - - write("ENUM Macros", - Fd, - m_enums(PREFIX, false, get_value(enum, Spec))), - write("DEFINE Macros", - Fd, - m_enums(PREFIX, false, get_value(define, Spec))), - - lists:foreach(fun({M,Es}) -> - write("ENUM Macros from " ++ atom_to_list(M), - Fd, - m_enums(PREFIX, true, Es)) - end, - get_value(import_enums, Spec)), - - file:close(Fd). - -forms(_, _, []) -> - ok; -forms(Banner, Fd, Forms) -> - write(Banner, Fd, prettypr(Forms)). - -write(_, _, []) -> - ok; -write(Banner, Fd, Str) -> - banner(Fd, Banner), - io:fwrite(Fd, "~s~n", [Str]). + = make_record_forms(ParseD), + + [hrl_header(Mod), + forms("Message records", MsgRecs), + forms("Grouped AVP records", GrpRecs), + lists:map(fun({M,Fs}) -> + forms("Grouped AVP records from " ++ atom_to_list(M), + Fs) + end, + ImportedGrpRecs), + format("ENUM Macros", m_enums(Prefix, false, get_value(enum, ParseD))), + format("DEFINE Macros", m_enums(Prefix, false, get_value(define, ParseD))), + lists:map(fun({M,Es}) -> + format("ENUM Macros from " ++ atom_to_list(M), + m_enums(Prefix, true, Es)) + end, + get_value(import_enums, ParseD))]. + +forms(_, [] = No) -> + No; +forms(Banner, Forms) -> + format(Banner, prettypr(Forms)). + +format(_, [] = No) -> + No; +format(Banner, Str) -> + [banner(Banner), Str, $\n]. prettypr(Forms) -> erl_prettypr:format(erl_syntax:form_list(Forms)). -banner(Fd, Heading) -> - file:write(Fd, banner(Heading)). - banner(Heading) -> - ("\n\n" + ["\n\n" "%%% -------------------------------------------------------\n" - "%%% " ++ Heading ++ ":\n" - "%%% -------------------------------------------------------\n\n"). + "%%% ", Heading, ":\n" + "%%% -------------------------------------------------------\n\n"]. z(S) -> string:join(string:tokens(S, "\s\t"), "\s"). @@ -845,8 +822,8 @@ arity([_], '*' = Inf) -> {0, Inf}; arity({_}, '*' = Inf) -> {1, Inf}; arity(_, {_,_} = Q) -> Q. -prefix(Spec) -> - case orddict:find(prefix, Spec) of +prefix(ParseD) -> + case orddict:find(prefix, ParseD) of {ok, P} -> P ++ "_"; error -> @@ -855,3 +832,70 @@ prefix(Spec) -> rec_name(Name, Prefix) -> Prefix ++ Name. + +%% =========================================================================== +%% pp/1 +%% +%% Preprocess forms as generated by 'forms' option. In particular, +%% replace the include_lib attributes in generated forms by the +%% corresponding forms, extracting the latter from an existing +%% dictionary (diameter_gen_relay). The resulting forms can be +%% compiled to beam using compile:forms/2 (which does no preprocessing +%% or it's own; DiY currently appears to be the only way to preprocess +%% a forms list). + +pp(Forms) -> + {_, Beam, _} = code:get_object_code(diameter_gen_relay), + pp(Forms, abstract_code(Beam)). + +pp(Forms, {ok, Code}) -> + Files = files(Code, []), + lists:flatmap(fun(T) -> include(T, Files) end, Forms); + +pp(Forms, {error, Reason}) -> + erlang:error({forms, Reason, Forms}). + +include({attribute, _, include_lib, Path}, Files) -> + Inc = filename:basename(Path), + [{Inc, Forms}] = [T || {F, _} = T <- Files, F == Inc], %% expect one + lists:flatmap(fun filter/1, Forms); + +include(T, _) -> + [T]. + +abstract_code(Beam) -> + case beam_lib:chunks(Beam, [abstract_code]) of + {ok, {_Mod, [{abstract_code, {_Vsn, Code}}]}} -> + {ok, Code}; + {ok, {_Mod, [{abstract_code, no_abstract_code = No}]}} -> + {error, No}; + {error = E, beam_lib, Reason} -> + {E, Reason} + end. + +files([{attribute, _, file, {Path, _}} | T], Acc) -> + {Body, Rest} = lists:splitwith(fun({attribute, _, file, _}) -> false; + (_) -> true + end, + T), + files(Rest, [{filename:basename(Path), Body} | Acc]); + +files([], Acc) -> + Acc. + +%% Only retain record diameter_avp and functions not generated by +%% diameter_exprecs. + +filter({attribute, _, record, {diameter_avp, _}} = T) -> + [T]; + +filter({function, _, Name, _, _} = T) -> + case ?S(Name) of + [$#|_] -> %% generated by diameter_exprecs + []; + _ -> + [T] + end; + +filter(_) -> + []. diff --git a/lib/diameter/src/compiler/diameter_dict_util.erl b/lib/diameter/src/compiler/diameter_dict_util.erl index 36a6efa294..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-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 @@ -46,7 +46,7 @@ -spec parse(File, Opts) -> {ok, orddict:orddict()} | {error, term()} - when File :: {path, string()} + when File :: {path, file:name_all()} | iolist() | binary(), Opts :: list(). @@ -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"; @@ -265,6 +267,9 @@ io(K, Id) io(vendor = K, {Id, Name}) -> [?NL, section(K) | [[?SP, tok(X)] || X <- [Id, Name]]]; +io(_, []) -> + []; + io(avp_types = K, Body) -> [?NL, ?NL, section(K), ?NL, [body(K,A) || A <- Body]]; @@ -398,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 @@ -1165,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, @@ -1273,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), @@ -1295,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_make.erl b/lib/diameter/src/compiler/diameter_make.erl index 16e30c1ffb..adc7808e49 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-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 @@ -30,38 +30,58 @@ -module(diameter_make). --export([codec/1, - codec/2, - dict/1, - dict/2, +-export([codec/2, + codec/1, format/1, - reformat/1]). + flatten/1, + format_error/1]). -export_type([opt/0]). +-include("diameter_vsn.hrl"). + +%% Options passed to codec/2. -type opt() :: {include|outdir|name|prefix|inherits, string()} + | return | verbose - | debug. + | parse %% internal parsed form + | forms %% abstract format for compile:forms/1,2 + | erl + | hrl. + +%% Internal parsed format with a version tag. +-type parsed() :: list(). + +%% Literal dictionary or path. A NL of CR identifies the former. +-type dict() :: iolist() + | binary() + | parsed(). %% as returned by codec/2 + +%% Name of a literal dictionary if otherwise unspecified. +-define(DEFAULT_DICT_FILE, "dictionary.dia"). %% =========================================================================== %% codec/1-2 %% -%% Parse a dictionary file and generate a codec module. +%% Parse a dictionary file and generate a codec module. Input +%% dictionary can be either a path or the dictionary itself: the +%% occurrence of \n or \r in the argument is used to distinguish the +%% two. --spec codec(Path, [opt()]) +-spec codec(File, [opt()]) -> ok + | {ok, list()} %% with option 'return', one element for each output | {error, Reason} - when Path :: string(), + when File :: dict() + | {path, file:name_all()}, Reason :: string(). codec(File, Opts) -> - case dict(File, Opts) of - {ok, Dict} -> - make(File, - Opts, - Dict, - [spec || _ <- [1], lists:member(debug, Opts)] ++ [erl, hrl]); + {Dict, Path} = identify(File), + case parse(Dict, Opts) of + {ok, ParseD} -> + make(Path, default(Opts), ParseD); {error, _} = E -> E end. @@ -69,63 +89,178 @@ codec(File, Opts) -> codec(File) -> codec(File, []). -%% dict/2 -%% -%% Parse a dictionary file and return the orddict that a codec module -%% returns from dict/0. - --spec dict(string(), [opt()]) - -> {ok, orddict:orddict()} - | {error, string()}. - -dict(Path, Opts) -> - case diameter_dict_util:parse({path, Path}, Opts) of - {ok, _} = Ok -> - Ok; - {error = E, Reason} -> - {E, diameter_dict_util:format_error(Reason)} - end. - -dict(File) -> - dict(File, []). - %% format/1 %% -%% Turn an orddict returned by dict/1-2 back into a dictionary file -%% in the form of an iolist(). +%% Turn an orddict returned by dict/1-2 back into a dictionary. --spec format(orddict:orddict()) +-spec format(parsed()) -> iolist(). -format(Dict) -> +format([?VERSION | Dict]) -> diameter_dict_util:format(Dict). -%% reformat/1 +%% flatten/1 %% -%% Parse a dictionary file and return its formatted equivalent. +%% Reconstitute a dictionary without @inherits. --spec reformat(File) - -> {ok, iolist()} - | {error, Reason} - when File :: string(), - Reason :: string(). +-spec flatten(parsed()) + -> parsed(). + +flatten([?VERSION = V | Dict]) -> + [V | lists:foldl(fun flatten/2, + Dict, + [avp_vendor_id, + custom_types, + codecs, + [avp_types, import_avps], + [grouped, import_groups], + [enum, import_enums]])]. + +%% format_error/1 + +format_error(T) -> + diameter_dict_util:format_error(T). + +%% =========================================================================== + +%% flatten/2 + +flatten([_,_] = Keys, Dict) -> + [Values, Imports] = [orddict:fetch(K, Dict) || K <- Keys], + Vs = lists:append([Values | [V || {_Mod, V} <- Imports]]), + lists:foldl(fun({K,V},D) -> orddict:store(K,V,D) end, + Dict, + lists:zip([inherits | Keys], [[], Vs, []])); + +%% Inherited avp's setting the 'V' flag get their value either from +%% @avp_vendor_id in the inheriting dictionary or from @vendor in the +%% *inherited* (not inheriting) dictionary: add the latter to +%% @avp_vendor_id as required. +flatten(avp_vendor_id = Key, Dict) -> + Def = orddict:find(vendor, Dict), + ModD = imports(Dict), + Vids = orddict:fetch(Key, Dict), + Avps = lists:append([As || {_,As} <- Vids]), + orddict:store(Key, + dict:fold(fun(M, As, A) -> vid(M, As -- Avps, Def, A) end, + Vids, + ModD), + Dict); + +%% Import @codecs and @custom_types from inherited dictionaries as +%% required. +flatten(Key, Dict) -> + ImportAvps = orddict:fetch(import_avps, Dict), + ImportItems = [{M, As} + || {Mod, Avps} <- ImportAvps, + [_|D] <- [Mod:dict()], + {M,As0} <- orddict:fetch(Key, D), + F <- [fun(A) -> lists:keymember(A, 1, Avps) end], + [_|_] = As <- [lists:filter(F, As0)]], + orddict:store(Key, + lists:foldl(fun merge/2, + orddict:fetch(Key, Dict), + ImportItems), + Dict). + +%% merge/2 + +merge({Mod, _Avps} = T, Acc) -> + merge(lists:keyfind(Mod, 1, Acc), T, Acc). -reformat(File) -> - case dict(File) of - {ok, Dict} -> - {ok, format(Dict)}; - {error, _} = No -> - No +merge({Mod, Avps}, {Mod, As}, Acc) -> + lists:keyreplace(Mod, 1, Acc, {Mod, Avps ++ As}); +merge(false, T, Acc) -> + [T | Acc]. + +%% imports/1 +%% +%% Return a module() -> [AVP] dict of inherited AVP's setting the V flag. + +imports(Dict) -> + lists:foldl(fun imports/2, + dict:new(), + orddict:fetch(import_avps, Dict)). + +imports({Mod, Avps}, Dict) -> + dict:store(Mod, + [A || {A,_,_,Fs} <- Avps, lists:member($V, Fs)], + Dict). + +%% vid/4 + +vid(_, [], _, Acc) -> + Acc; +vid(Mod, Avps, Def, Acc) -> + v(Mod:vendor_id(), Avps, Def, Acc). + +v(Vid, _, {ok, {Vid, _}}, Acc) -> %% same id as inheriting dictionary's + Acc; +v(Vid, Avps, _, Acc) -> + case lists:keyfind(Vid, 1, Acc) of + {Vid, As} -> + lists:keyreplace(Vid, 1, Acc, {Vid, As ++ Avps}); + false -> + [{Vid, Avps} | Acc] end. %% =========================================================================== -make(_, _, _, []) -> +parse({dict, ParseD}, _) -> + {ok, ParseD}; +parse(File, Opts) -> + diameter_dict_util:parse(File, Opts). + +default(Opts) -> + def(modes(Opts), Opts). + +def([], Opts) -> + [erl, hrl | Opts]; +def(_, Opts) -> + Opts. + +modes(Opts) -> + lists:filter(fun is_mode/1, Opts). + +is_mode(T) -> + lists:member(T, [erl, hrl, parse, forms]). + +identify([Vsn | [T|_] = ParseD]) + when is_tuple(T) -> + ?VERSION == Vsn orelse erlang:error({version, {Vsn, ?VERSION}}), + {{dict, ParseD}, ?DEFAULT_DICT_FILE}; +identify({path, File} = T) -> + {T, File}; +identify(File) -> + Bin = iolist_to_binary([File]), + case is_path(Bin) of + true -> {{path, File}, File}; + false -> {Bin, ?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. + +make(File, Opts, Dict) -> + ok(lists:foldl(fun(M,A) -> [make(File, Opts, Dict, M) | A] end, + [], + modes(Opts))). + +ok([ok|_]) -> ok; -make(File, Opts, Dict, [Mode | Rest]) -> +ok([_|_] = L) -> + {ok, lists:reverse(L)}. + +make(File, Opts, Dict, Mode) -> try - ok = diameter_codegen:from_dict(File, Dict, Opts, Mode), - make(File, Opts, Dict, Rest) + diameter_codegen:from_dict(File, Dict, Opts, Mode) catch error: Reason -> erlang:error({Reason, Mode, erlang:get_stacktrace()}) diff --git a/lib/diameter/src/diameter.appup.src b/lib/diameter/src/diameter.appup.src index 62ace16faa..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,59 +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", [{load_module, diameter_reg}, %% R16B - {load_module, diameter_stats}, - {load_module, diameter_traffic}, - {load_module, diameter_service}, - {load_module, diameter_config}, - {load_module, diameter_peer}, - {load_module, diameter_peer_fsm}, - {load_module, diameter_watchdog}, - {load_module, diameter_capx}, - {load_module, diameter_codec}, - {load_module, diameter_gen_base_rfc3588}, - {load_module, diameter_gen_base_accounting}, - {load_module, diameter_gen_base_rfc6733}, - {load_module, diameter_gen_base_acct6733}, - {load_module, diameter_tcp}, - {load_module, diameter_lib}, - {load_module, diameter}]}, - {"1.4.1.1", [{load_module, diameter_traffic}, - {load_module, diameter_service}, - {load_module, diameter_config}, - {load_module, diameter_peer}, - {load_module, diameter_peer_fsm}, - {load_module, diameter_watchdog}, - {load_module, diameter_capx}, - {load_module, diameter_codec}, - {load_module, diameter_gen_base_rfc3588}, - {load_module, diameter_gen_base_accounting}, - {load_module, diameter_gen_base_rfc6733}, - {load_module, diameter_gen_base_acct6733}, - {load_module, diameter_tcp}, - {load_module, diameter_lib}, - {load_module, diameter}]} + {"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", [{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}]}, - {"1.4.1.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", [{restart_application, diameter}]}, + {"1.4.4", [{restart_application, diameter}]}, + {"1.5", [{restart_application, diameter}]} ] }. 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) |