diff options
Diffstat (limited to 'lib/diameter')
-rw-r--r-- | lib/diameter/doc/src/diameter.xml | 37 | ||||
-rw-r--r-- | lib/diameter/doc/src/diameter_app.xml | 47 | ||||
-rw-r--r-- | lib/diameter/doc/src/diameter_sctp.xml | 10 | ||||
-rw-r--r-- | lib/diameter/doc/src/diameter_tcp.xml | 13 | ||||
-rw-r--r-- | lib/diameter/include/diameter_gen.hrl | 4 | ||||
-rw-r--r-- | lib/diameter/src/app/Makefile | 4 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_codec.erl | 22 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_config.erl | 11 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_internal.hrl | 23 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_lib.erl | 28 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_peer.erl | 11 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_reg.erl | 12 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_service.erl | 200 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_stats.erl | 11 | ||||
-rw-r--r-- | lib/diameter/src/app/diameter_sync.erl | 35 | ||||
-rw-r--r-- | lib/diameter/src/compiler/diameter_codegen.erl | 1 |
16 files changed, 215 insertions, 254 deletions
diff --git a/lib/diameter/doc/src/diameter.xml b/lib/diameter/doc/src/diameter.xml index 36b6cbf0cf..2cad70e3bc 100644 --- a/lib/diameter/doc/src/diameter.xml +++ b/lib/diameter/doc/src/diameter.xml @@ -277,6 +277,10 @@ callback.</p> </taglist> +<p> +An invalid option will cause <seealso marker="#call">call/4</seealso> +to fail.</p> + <marker id="capability"/> </item> @@ -405,6 +409,8 @@ sense.</p> <code> eval([{M,F,A} | T]) -> apply(M, F, T ++ A); +eval([[F|A] | T]) -> + eval([F | T ++ A]); eval([F|A]) -> apply(F, A); eval(F) -> @@ -461,14 +467,14 @@ or any peer if the request does not contain a <c>Destination-Realm</c> AVP.</p> </item> -<tag><c>{host, any|UTF8String()}</c></tag> +<tag><c>{host, any|DiameterIdentity()}</c></tag> <item> <p> Matches only those peers whose <c>Origin-Host</c> has the specified value, or all peers if the atom <c>any</c>.</p> </item> -<tag><c>{realm, any|UTF8String()</c></tag> +<tag><c>{realm, any|DiameterIdentity()</c></tag> <item> <p> Matches only those peers whose <c>Origin-Realm</c> has the @@ -478,8 +484,9 @@ value, or all peers if the atom <c>any</c>.</p> <tag><c>{eval, evaluable()}</c></tag> <item> <p> -Matches only those peers for which the specified evaluable() evaluates -to true on the peer's <c>diameter_caps</c> record.</p> +Matches only those peers for which the specified evaluable() returns +<c>true</c> on the connection's <c>diameter_caps</c> record. +Any other return value or exception is equivalent to <c>false</c>.</p> </item> <tag><c>{neg, peer_filter()}</c></tag> @@ -503,6 +510,21 @@ specified list.</p> </taglist> +<p> +Note that the <c>host</c> and <c>realm</c> filters examine the +outgoing request as passed to <seealso marker="#call">call/4</seealso>, +assuming that this is a record- or list-valued message() as documented +in <seealso marker="diameter_app">diameter_app(3)</seealso>, and that +the message contains at most one of each AVP. +If this is not the case then the <c>{host|realm, DiameterIdentity()}</c> +filters must be used to achieve the desired result. +Note also that an empty host/realm (which should not be typical) +is equivalent to an unspecified one for the purposes of filtering.</p> + +<p> +An invalid filter is equivalent to <c>{any, []}</c>, a filter +that matches no peer.</p> + <marker id="service_event"/> </item> @@ -787,7 +809,7 @@ transports.</p> <type> <v>SvcName = service_name()</v> <v>App = application_alias()</v> -<v>Request = diameter_app:message()</v> +<v>Request = diameter_app:message() | term()</v> <v>Answer = term()</v> <v>Options = [call_opt()]</v> </type> @@ -819,9 +841,8 @@ If there are no suitable peers, or if <seealso marker="diameter_app#pick_peer">pick_peer/4</seealso> rejects them by returning 'false', then <c>{error, no_connection}</c> is returned. -If <seealso marker="diameter_app#pick_peer">pick_peer/4</seealso> -selects a candidate peer then a request process is spawned for the -outgoing request, in which there is a +Otherwise <seealso marker="diameter_app#pick_peer">pick_peer/4</seealso> +is followed by a <seealso marker="diameter_app#prepare_request">prepare_request/3</seealso> callback, the message is encoded and sent.</p> diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml index fc359b9d1d..a9ae0ebbec 100644 --- a/lib/diameter/doc/src/diameter_app.xml +++ b/lib/diameter/doc/src/diameter_app.xml @@ -269,7 +269,12 @@ The candidate peers list will only include those which are selected by any <c>filter</c> option specified in the call to <seealso marker="diameter#call">diameter:call/4</seealso>, and only those which have indicated support for the Diameter application in -question.</p> +question. +The order of the elements is unspecified except that any +peers whose Origin-Host and Origin-Realm matches that of the +outgoing request (in the sense of a <c>{filter, {all, [host, realm]}}</c> +option to <seealso marker="diameter#call">diameter:call/4</seealso>) +will be placed at the head of the list.</p> <p> The return values <c>false</c> and <c>{false, State}</c> are @@ -467,11 +472,11 @@ callback returned false.</p> <v>Packet = packet()</v> <v>SvcName = term()</v> <v>Peer = peer()</v> -<v>Action = Reply | {relay, Opts} | discard | {eval, Action, ContF}</v> +<v>Action = Reply | {relay, Opts} | discard | {eval, Action, PostF}</v> <v>Reply = {reply, message()} | {protocol_error, 3000..3999}</v> <v>Opts = diameter:call_opts()</v> -<v>ContF = diameter:evaluable()</v> +<v>PostF = diameter:evaluable()</v> </type> <desc> <p> @@ -559,26 +564,28 @@ will cause the request process in question to fail.</p> <tag><c>{relay, Opts}</c></tag> <item> <p> -Relay a request to another peer. -The appropriate Route-Record AVP will be added to the relayed request -by diameter and <seealso marker="#pick_peer">pick_peer/4</seealso> -and <seealso marker="#prepare_request">prepare_request/3</seealso> -callback will take place just as if <seealso +Relay a request to another peer in the role of a Diameter relay agent. +If a routing loop is detected then the request is answered with +3005 (DIAMETER_LOOP_DETECTED). +Otherwise a Route-Record AVP (containing the sending peer's Origin-Host) is +added to the request and <seealso marker="#pick_peer">pick_peer/4</seealso> +and subsequent callbacks take place just as if <seealso marker="diameter#call">diameter:call/4</seealso> had been called explicitly. -However, returning a <c>relay</c> tuple also causes the End-to-End -Identifier to be preserved in the header of the relayed request as -required by RFC 3588.</p> +The End-to-End Identifier of the incoming request is preserved in the +header of the relayed request.</p> <p> -The returned <c>Opts</c> should not specify <c>detach</c> and -the <seealso marker="#handle_answer">handle_answer/4</seealso> -callback following from a relayed request must return its first +The returned <c>Opts</c> should not specify <c>detach</c>. +A subsequent <seealso marker="#handle_answer">handle_answer/4</seealso> +callback for the relayed request must return its first argument, the <c>diameter_packet</c> record containing the answer message. Note that the <c>extra</c> option can be specified to supply arguments -that can distinguish the relay case from others if so desired, -although the form of the request message may be sufficient.</p> +that can distinguish the relay case from others if so desired. +Any other return value (for example, from a +<seealso marker="#handle_error">handle_error/4</seealso> callback) +causes the request to be answered with 3002 (DIAMETER_UNABLE_TO_DELIVER).</p> </item> <tag><c>discard</c></tag> @@ -587,18 +594,18 @@ although the form of the request message may be sufficient.</p> Discard the request.</p> </item> -<tag><c>{eval, Action, ContF}</c></tag> +<tag><c>{eval, Action, PostF}</c></tag> <item> <p> Handle the request as if <c>Action</c> has been returned and then -evaluate <c>ContF</c> in the request process.</p> +evaluate <c>PostF</c> in the request process.</p> </item> </taglist> <p> -Note that diameter will respond to protocol errors in an incoming -request without invoking <c>handle_request/3</c>.</p> +Note that protocol errors detected by diameter will result in an +answer message without <c>handle_request/3</c> being invoked.</p> </desc> </func> diff --git a/lib/diameter/doc/src/diameter_sctp.xml b/lib/diameter/doc/src/diameter_sctp.xml index d0377f4b38..c1e839b8e1 100644 --- a/lib/diameter/doc/src/diameter_sctp.xml +++ b/lib/diameter/doc/src/diameter_sctp.xml @@ -74,11 +74,12 @@ marker="diameter_transport#start">diameter_transport(3)</seealso>.</p> <p> The only diameter_sctp-specific argument is the options list. Options <c>raddr</c> and <c>rport</c> specify the remote address -and port for a connector and not valid for a listener. +and port for a connecting transport and not valid for a listening +transport. The former is required while latter defaults to 3868 if unspecified. More than one <c>raddr</c> option can be specified, in which case the -connector in question attempts each in sequence until an association -is established. +connecting transport in question attempts each in sequence until +an association is established. Remaining options are any accepted by gen_sctp:open/1, with the exception of options <c>mode</c>, <c>binary</c>, <c>list</c>, <c>active</c> and <c>sctp_events</c>. @@ -89,7 +90,8 @@ and port respectively.</p> Multiple <c>ip</c> options can be specified for a multihomed peer. If none are specified then the values of Host-IP-Address on the service are used. (In particular, one of these must be specified.) -Option <c>port</c> defaults to 3868 for a listener and 0 for a connector.</p> +Option <c>port</c> defaults to 3868 for a listening transport and 0 for a +connecting transport.</p> <p> diameter_sctp uses the <c>transport_data</c> field of diff --git a/lib/diameter/doc/src/diameter_tcp.xml b/lib/diameter/doc/src/diameter_tcp.xml index 5d6e07b1b8..a502e53972 100644 --- a/lib/diameter/doc/src/diameter_tcp.xml +++ b/lib/diameter/doc/src/diameter_tcp.xml @@ -74,13 +74,14 @@ marker="diameter_transport#start">diameter_transport(3)</seealso>.</p> <p> The only diameter_tcp-specific argument is the options list. Options <c>raddr</c> and <c>rport</c> specify the remote address -and port for a connector and not valid for a listener. +and port for a connecting transport and not valid for a listening +transport. Remaining options are any accepted by gen_tcp:connect/3 for -a connector, or gen_tcp:listen/2 for a listener, with the exception -of <c>binary</c>, <c>packet</c> and <c>active</c>. -Also, option <c>port</c> can be specified for a listener to specify the -local listening port, the default being the standardized 3868 if -unspecified. +a connecting transport, or gen_tcp:listen/2 for a listening transport, +with the exception of <c>binary</c>, <c>packet</c> and <c>active</c>. +Also, option <c>port</c> can be specified for a listening transport +to specify the local listening port, the default being the standardized +3868 if unspecified. Note that option <c>ip</c> specifies the local address.</p> <p> diff --git a/lib/diameter/include/diameter_gen.hrl b/lib/diameter/include/diameter_gen.hrl index 4c91954a21..d037e1044a 100644 --- a/lib/diameter/include/diameter_gen.hrl +++ b/lib/diameter/include/diameter_gen.hrl @@ -44,14 +44,14 @@ encode_avps(Name, Rec) -> ?MODULE, ?LINE, {Reason, Name, Rec}), - erlang:error(list_to_tuple(Reason ++ [Name, Rec, ?MODULE])); + erlang:error(list_to_tuple(Reason ++ [Name])); error: Reason -> Stack = erlang:get_stacktrace(), diameter_dbg:log({encode, failure}, ?MODULE, ?LINE, {Reason, Name, Rec, Stack}), - erlang:error({encode_failure, Reason, Name, Rec, ?MODULE, Stack}) + erlang:error({encode_failure, Reason, Name, Stack}) end. %% encode/2 diff --git a/lib/diameter/src/app/Makefile b/lib/diameter/src/app/Makefile index 6de220d282..229466e264 100644 --- a/lib/diameter/src/app/Makefile +++ b/lib/diameter/src/app/Makefile @@ -181,6 +181,10 @@ release_docs_spec: # Dependencies # ---------------------------------------------------- +$(SPEC_FILES:%.dia=$(EBIN)/%.$(EMULATOR)): \ + $(DIAMETER_TOP)/include/diameter.hrl \ + $(DIAMETER_TOP)/include/diameter_gen.hrl + depend: depend.mk # Generate dependencies makefile. It's assumed that the compile target diff --git a/lib/diameter/src/app/diameter_codec.erl b/lib/diameter/src/app/diameter_codec.erl index f6cbde5446..d88f42fb7c 100644 --- a/lib/diameter/src/app/diameter_codec.erl +++ b/lib/diameter/src/app/diameter_codec.erl @@ -140,10 +140,10 @@ make_flags(Flags0, #diameter_header{is_request = R, mf(undefined, F, _) -> F; mf(B, F, N) -> %% reset the affected bit - (F bxor (F band (1 bsl N))) bor (bit(B) bsl N). + (F bxor (F band (1 bsl N))) bor bit(B, N). -bit(true) -> 1; -bit(false) -> 0. +bit(true, N) -> 1 bsl N; +bit(false, _) -> 0. %% values/1 @@ -199,25 +199,16 @@ msg_header(Mod, MsgName, Header) -> p(Flags, #diameter_header{is_request = true, is_proxiable = P}) -> - Flags bor choose(P, 2#01000000, 0); + Flags band (2#10110000 bor choose(P, 2#01000000, 0)); p(Flags, _) -> Flags. -%% The header below is that of the incoming request being answered, -%% not of the answer (which hasn't been encoded yet). - h(Mod, 'answer-message' = MsgName, Header) -> ?BASE = Mod, - #diameter_header{is_request = true, - cmd_code = Code} - = Header, + #diameter_header{cmd_code = Code} = Header, {_, Flags, ApplId} = ?BASE:msg_header(MsgName), {Code, Flags, ApplId}; -h(Mod, MsgName, #diameter_header{is_request = true, - cmd_code = Code}) -> - {Code, _, _} = Mod:msg_header(MsgName); %% ensure Code - h(Mod, MsgName, _) -> Mod:msg_header(MsgName). @@ -290,7 +281,8 @@ decode_avps(MsgName, Mod, Pkt, {Bs, Avps}) -> %% invalid avp bits ... decode_avps('', Mod, Pkt, Avps) -> %% unknown message ... ?LOG(unknown, {Mod, Pkt#diameter_packet.header}), - Pkt#diameter_packet{errors = lists:reverse(Avps)}; + Pkt#diameter_packet{avps = lists:reverse(Avps), + errors = [3001]}; %% DIAMETER_COMMAND_UNSUPPORTED %% msg = undefined identifies this case. decode_avps(MsgName, Mod, Pkt, Avps) -> %% ... or not diff --git a/lib/diameter/src/app/diameter_config.erl b/lib/diameter/src/app/diameter_config.erl index 42c70890b3..a6b48fe65b 100644 --- a/lib/diameter/src/app/diameter_config.erl +++ b/lib/diameter/src/app/diameter_config.erl @@ -267,7 +267,7 @@ handle_call(uptime, _, #state{id = Time} = State) -> {reply, diameter_lib:now_diff(Time), State}; handle_call(Req, From, State) -> - warning_msg("received unexpected request from ~p:~n~w", [From, Req]), + ?UNEXPECTED([Req, From]), Reply = {error, {bad_request, Req}}, {reply, Reply, State}. @@ -276,7 +276,7 @@ handle_call(Req, From, State) -> %%% ---------------------------------------------------------- handle_cast(Msg, State) -> - warning_msg("received unexpected message:~n~w", [Msg]), + ?UNEXPECTED([Msg]), {noreply, State}. %%% ---------------------------------------------------------- @@ -309,7 +309,7 @@ handle_info(restart, State) -> {noreply, State}; handle_info(Info, State) -> - warning_msg("received unknown info:~n~w", [Info]), + ?UNEXPECTED([Info]), {noreply, State}. %%-------------------------------------------------------------------- @@ -674,8 +674,3 @@ cb(M,F) -> call(Request) -> gen_server:call(?SERVER, Request, infinity). - -%% warning_msg/2 - -warning_msg(F, A) -> - ?diameter_warning("~p: " ++ F, [?MODULE | A]). diff --git a/lib/diameter/src/app/diameter_internal.hrl b/lib/diameter/src/app/diameter_internal.hrl index 9de3914830..78645b1912 100644 --- a/lib/diameter/src/app/diameter_internal.hrl +++ b/lib/diameter/src/app/diameter_internal.hrl @@ -37,9 +37,10 @@ %% Failure reports always get a stack trace. -define(STACK, erlang:get_stacktrace()). -%% Info report for anything unexpected. --define(REPORT(Reason, Func, Args), - diameter_lib:report(Reason, {?MODULE, Func, Args})). +%% Warning report for unexpected messages in various processes. +-define(UNEXPECTED(F,A), + diameter_lib:warning_report(unexpected, {?MODULE, F, A})). +-define(UNEXPECTED(A), ?UNEXPECTED(?FUNC, A)). %% Something to trace on. -define(LOG(Slogan, Details), @@ -77,19 +78,3 @@ server_id, is_dynamic, expiration}). - -%%%---------------------------------------------------------------------- -%%% Error/warning/info message macro(s) -%%%---------------------------------------------------------------------- - --define(diameter_info(F, A), - (catch error_logger:info_msg("[ ~w : ~w : ~p ] ~n" ++ F ++ "~n", - [?APPLICATION, ?MODULE, self()|A]))). - --define(diameter_warning(F, A), - (catch error_logger:warning_msg("[ ~w : ~w : ~p ] ~n" ++ F ++ "~n", - [?APPLICATION, ?MODULE, self()|A]))). - --define(diameter_error(F, A), - (catch error_logger:error_msg("[ ~w : ~w : ~p ] ~n" ++ F ++ "~n", - [?APPLICATION, ?MODULE, self()|A]))). diff --git a/lib/diameter/src/app/diameter_lib.erl b/lib/diameter/src/app/diameter_lib.erl index b5c0e1bf6a..f65e356444 100644 --- a/lib/diameter/src/app/diameter_lib.erl +++ b/lib/diameter/src/app/diameter_lib.erl @@ -46,14 +46,9 @@ report(Reason, MFA) -> info_report(Reason, MFA). -info_report(Reason, {M,F,A}) -> - error_logger:info_report(" Reason: ~p~n" - " Pid: ~p~n" - " Node: ~p~n" - " Module: ~p~n" - " Function: ~p~n" - "Arguments: ~p~n", - [Reason, self(), node(), M, F, A]). +info_report(Reason, MFA) -> + report(fun error_logger:info_report/1, Reason, MFA), + true. %%% --------------------------------------------------------------------------- %%% # error_report(Reason, MFA) @@ -69,7 +64,7 @@ warning_report(Reason, MFA) -> report(fun error_logger:warning_report/1, Reason, MFA). report(Fun, Reason, MFA) -> - Fun([{reason, Reason}, {who, self()}, {where, node()}, {what, MFA}]), + Fun([{why, Reason}, {who, self()}, {what, MFA}]), false. %%% --------------------------------------------------------------------------- @@ -255,12 +250,13 @@ w(L) -> fold_tuple(_, T, undefined) -> T; -fold_tuple(N, T0, T) -> - element(2, lists:foldl(fun(X, {M,_} = A) -> {M+1, ft(X, A)} end, - {N, T0}, - lists:nthtail(N-1, tuple_to_list(T)))). +fold_tuple(N, T0, T1) -> + {_, T} = lists:foldl(fun(V, {I,_} = IT) -> {I+1, ft(V, IT)} end, + {N, T0}, + lists:nthtail(N-1, tuple_to_list(T1))), + T. -ft(undefined, T) -> +ft(undefined, {_, T}) -> T; -ft(X, {N, T}) -> - setelement(N, T, X). +ft(Value, {Idx, T}) -> + setelement(Idx, T, Value). diff --git a/lib/diameter/src/app/diameter_peer.erl b/lib/diameter/src/app/diameter_peer.erl index 6b8971b8ea..3e78c4caef 100644 --- a/lib/diameter/src/app/diameter_peer.erl +++ b/lib/diameter/src/app/diameter_peer.erl @@ -148,7 +148,7 @@ handle_call(uptime, _, #state{id = Time} = State) -> {reply, diameter_lib:now_diff(Time), State}; handle_call(Req, From, State) -> - warning_msg("received unexpected request from ~p:~n~w", [From, Req]), + ?UNEXPECTED([Req, From]), {reply, nok, State}. %%% ---------------------------------------------------------- @@ -156,7 +156,7 @@ handle_call(Req, From, State) -> %%% ---------------------------------------------------------- handle_cast(Msg, State) -> - warning_msg("received unexpected message:~n~w", [Msg]), + ?UNEXPECTED([Msg]), {noreply, State}. %%% ---------------------------------------------------------- @@ -169,7 +169,7 @@ handle_info({notify, SvcName, T}, S) -> {noreply, S}; handle_info(Info, State) -> - warning_msg("received unexpected info:~n~w", [Info]), + ?UNEXPECTED([Info]), {noreply, State}. %% ---------------------------------------------------------- @@ -223,8 +223,3 @@ value([], V) -> call(Request) -> gen_server:call(?SERVER, Request, infinity). - -%% warning_msg/2 - -warning_msg(F, A) -> - ?diameter_warning("~p: " ++ F, [?MODULE | A]). diff --git a/lib/diameter/src/app/diameter_reg.erl b/lib/diameter/src/app/diameter_reg.erl index 8e5f34c2c3..882b9da238 100644 --- a/lib/diameter/src/app/diameter_reg.erl +++ b/lib/diameter/src/app/diameter_reg.erl @@ -243,7 +243,8 @@ handle_call(state, _, State) -> handle_call(uptime, _, #state{id = Time} = State) -> {reply, diameter_lib:now_diff(Time), State}; -handle_call(_Req, _From, State) -> +handle_call(Req, From, State) -> + ?UNEXPECTED([Req, From]), {reply, nok, State}. %%% ---------------------------------------------------------- @@ -251,7 +252,7 @@ handle_call(_Req, _From, State) -> %%% ---------------------------------------------------------- handle_cast(Msg, State)-> - warning_msg("received unexpected message:~n~w", [Msg]), + ?UNEXPECTED([Msg]), {noreply, State}. %%% ---------------------------------------------------------- @@ -264,7 +265,7 @@ handle_info({'DOWN', MRef, process, Pid, _}, State) -> {noreply, State}; handle_info(Info, State) -> - warning_msg("received unknown info:~n~w", [Info]), + ?UNEXPECTED([Info]), {noreply, State}. %%% ---------------------------------------------------------- @@ -324,8 +325,3 @@ repl([], _, _) -> call(Request) -> gen_server:call(?SERVER, Request, infinity). - -%% warning_msg/2 - -warning_msg(F, A) -> - ?diameter_warning("~p: " ++ F, [?MODULE | A]). diff --git a/lib/diameter/src/app/diameter_service.erl b/lib/diameter/src/app/diameter_service.erl index 63b0649dc4..421e36ccf5 100644 --- a/lib/diameter/src/app/diameter_service.erl +++ b/lib/diameter/src/app/diameter_service.erl @@ -463,7 +463,7 @@ handle_call(stop, _From, S) -> %% stating a monitor that waits for DOWN before returning. handle_call(Req, From, S) -> - ?REPORT(unknown_request, ?FUNC, [Req, From]), + unexpected(handle_call, [Req, From], S), {reply, nok, S}. %%% --------------------------------------------------------------------------- @@ -471,7 +471,7 @@ handle_call(Req, From, S) -> %%% --------------------------------------------------------------------------- handle_cast(Req, S) -> - ?REPORT(unknown_request, ?FUNC, [Req]), + unexpected(handle_cast, [Req], S), {noreply, S}. %%% --------------------------------------------------------------------------- @@ -553,8 +553,8 @@ transition({failover, TRef, Seqs}, S) -> failover(TRef, Seqs, S), ok; -transition(Req, _) -> - ?REPORT(unknown_request, ?FUNC, [Req]), +transition(Req, S) -> + unexpected(handle_info, [Req], S), ok. %%% --------------------------------------------------------------------------- @@ -591,6 +591,9 @@ code_change(FromVsn, SvcName, Extra, #diameter_app{alias = Alias} = A) -> %% =========================================================================== %% =========================================================================== +unexpected(F, A, #state{service_name = Name}) -> + ?UNEXPECTED(F, A ++ [Name]). + cb([_|_] = M, F, A) -> eval(M, F, A); cb(Rec, F, A) -> @@ -1398,15 +1401,15 @@ recv_answer(Timeout, %% is, from the last peer to which we've transmitted. receive - {answer = A, Ref, Rq, Pkt} -> %% Answer from peer. + {answer = A, Ref, Rq, Pkt} -> %% Answer from peer {A, Rq, Pkt}; - {timeout = Reason, TRef, _} -> %% No timely reply + {timeout = Reason, TRef, _} -> %% No timely reply {error, Req, Reason}; - {failover = Reason, TRef, false} -> %% No alternative peer. + {failover = Reason, TRef, false} -> %% No alternate peer {error, Req, Reason}; - {failover, TRef, Transport} -> %% Resend to alternate peer. + {failover, TRef, Transport} -> %% Resend to alternate peer try_retransmit(Timeout, SvcName, Req, Transport); - {failover, TRef} -> %% May have missed failover notification. + {failover, TRef} -> %% May have missed failover notification Seqs = diameter_codec:sequence_numbers(RPkt), Pid = whois(SvcName), is_pid(Pid) andalso (Pid ! {failover, TRef, Seqs}), @@ -1685,9 +1688,9 @@ recv_request({Id, Alias}, T, TPid, Apps, Caps, Pkt) -> %% DIAMETER_APPLICATION_UNSUPPORTED 3007 %% A request was sent for an application that is not supported. -recv_request(false, {_, OH, OR}, TPid, _, _, Pkt) -> - ?LOG({error, application}, Pkt), - reply(answer_message({OH, OR, 3007}, collect_avps(Pkt)), ?BASE, TPid, Pkt). +recv_request(false, T, TPid, _, _, Pkt) -> + As = collect_avps(Pkt), + protocol_error(3007, T, TPid, Pkt#diameter_packet{avps = As}). collect_avps(Pkt) -> case diameter_codec:collect_avps(Pkt) of @@ -1706,13 +1709,9 @@ collect_avps(Pkt) -> %% set to an unrecognized value, or that is inconsistent with the %% AVP's definition. %% -recv_request({_, OH, OR}, {TPid, _}, _, #diameter_packet{errors = [Bs | _], - bin = Bin, - avps = Avps} - = Pkt) +recv_request(T, {TPid, _}, _, #diameter_packet{errors = [Bs | _]} = Pkt) when is_bitstring(Bs) -> - ?LOG({error, invalid_avp_bits}, Bin), - reply(answer_message({OH, OR, 3009}, Avps), ?BASE, TPid, Pkt); + protocol_error(3009, T, TPid, Pkt); %% Either we support this application but don't recognize the command %% or we're a relay and the command isn't proxiable. @@ -1722,18 +1721,15 @@ recv_request({_, OH, OR}, {TPid, _}, _, #diameter_packet{errors = [Bs | _], %% recognize or support. This MUST be used when a Diameter node %% receives an experimental command that it does not understand. %% -recv_request({_, OH, OR}, +recv_request(T, {TPid, _}, #diameter_app{id = Id}, #diameter_packet{header = #diameter_header{is_proxiable = P}, - msg = M, - avps = Avps, - bin = Bin} + msg = M} = Pkt) when ?APP_ID_RELAY /= Id, undefined == M; ?APP_ID_RELAY == Id, not P -> - ?LOG({error, command_unsupported}, Bin), - reply(answer_message({OH, OR, 3001}, Avps), ?BASE, TPid, Pkt); + protocol_error(3001, T, TPid, Pkt); %% Error bit was set on a request. %% @@ -1742,15 +1738,12 @@ recv_request({_, OH, OR}, %% either set to an invalid combination, or to a value that is %% inconsistent with the command code's definition. %% -recv_request({_, OH, OR}, +recv_request(T, {TPid, _}, _, - #diameter_packet{header = #diameter_header{is_error = true}, - avps = Avps, - bin = Bin} + #diameter_packet{header = #diameter_header{is_error = true}} = Pkt) -> - ?LOG({error, error_bit}, Bin), - reply(answer_message({OH, OR, 3008}, Avps), ?BASE, TPid, Pkt); + protocol_error(3008, T, TPid, Pkt); %% A message in a locally supported application or a proxiable message %% in the relay application. Don't distinguish between the two since @@ -1878,7 +1871,7 @@ resend(true, _, _, T, {TPid, _}, Pkt) -> %% Route-Record loop resend(false, Opts, App, - {SvcName, _, _}, + {SvcName, _, _} = T, {TPid, #diameter_caps{origin_host = {_, OH}}}, #diameter_packet{header = Hdr0, avps = Avps} @@ -1887,46 +1880,41 @@ resend(false, Seq = diameter_session:sequence(), Hdr = Hdr0#diameter_header{hop_by_hop_id = Seq}, Msg = [Hdr, Route | Avps], - %% Filter sender as ineligible receiver. - reply(call(SvcName, App, Msg, [{filter, {neg, {host, OH}}} | Opts]), - TPid, - Pkt). + resend(call(SvcName, App, Msg, Opts), T, TPid, Pkt). %% The incoming request is relayed with the addition of a -%% Route-Record. Note the requirement on the return from call/4. -%% This places a requirement on the values returned by the -%% handle_answer and handle_error callbacks of the application module -%% in question. +%% Route-Record. Note the requirement on the return from call/4 below, +%% which places a requirement on the value returned by the +%% handle_answer callback of the application module in question. +%% +%% Note that there's nothing stopping the request from being relayed +%% back to the sender. A pick_peer callback may want to avoid this but +%% a smart peer might recognize the potential loop and choose another +%% route. A less smart one will probably just relay the request back +%% again and force us to detect the loop. A pick_peer that wants to +%% avoid this can specify filter to avoid the possibility. +%% Eg. {neg, {host, OH} where #diameter_caps{origin_host = {OH, _}}. %% %% RFC 6.3 says that a relay agent does not modify Origin-Host but %% says nothing about a proxy. Assume it should behave the same way. -%% reply/3 +%% resend/4 %% %% Relay a reply to a relayed request. %% Answer from the peer: reset the hop by hop identifier and send. -reply(#diameter_packet{bin = B} - = Pkt, - TPid, - #diameter_packet{header = #diameter_header{hop_by_hop_id = Id}, - transport_data = TD}) -> +resend(#diameter_packet{bin = B} + = Pkt, + _, + TPid, + #diameter_packet{header = #diameter_header{hop_by_hop_id = Id}, + transport_data = TD}) -> send(TPid, Pkt#diameter_packet{bin = diameter_codec:hop_by_hop_id(Id, B), transport_data = TD}); %% TODO: counters -%% Not. Ignoring the error feels harsh but there is no appropriate -%% Result-Code for a protocol error (which this isn't really anyway) -%% and the RFC doesn't provide any guidance how to act. A weakness -%% here is that we don't deal well with a decode error: the request -%% will simply timeout on the peer's end. Better would be to just send -%% the answer (with modified hop by hop identifier) on regardless, at -%% least in the relay case in which there's no examination of the -%% answer. In the proxy case it's not clear that the callback won't -%% examine the answer. Just be quiet here since a decode error causes -%% the request process to crash (or not depending on the error and -%% config and/or handle_answer callback). -reply(_, _, _) -> - ok. +%% Or not: DIAMETER_UNABLE_TO_DELIVER. +resend(_, T, TPid, Pkt) -> + protocol_error(3002, T, TPid, Pkt). %% is_loop/4 %% @@ -1971,24 +1959,20 @@ reply(Msg, Dict, TPid, #diameter_packet{errors = [H|_] = Es} = Pkt) -> %% make_reply_packet/2 +%% Binaries and header/avp lists are sent as-is. make_reply_packet(Bin, _) when is_binary(Bin) -> #diameter_packet{bin = Bin}; - make_reply_packet([#diameter_header{} | _] = Msg, _) -> #diameter_packet{msg = Msg}; +%% Otherwise a reply message clears the R and T flags and retains the +%% P flag. The E flag will be set at encode. make_reply_packet(Msg, #diameter_packet{header = ReqHdr}) -> - #diameter_header{end_to_end_id = EId, - hop_by_hop_id = Hid, - is_proxiable = P} - = ReqHdr, - - Hdr = #diameter_header{version = ?DIAMETER_VERSION, - end_to_end_id = EId, - hop_by_hop_id = Hid, - is_proxiable = P, - is_retransmitted = false}, + Hdr = ReqHdr#diameter_header{version = ?DIAMETER_VERSION, + is_request = false, + is_error = undefined, + is_retransmitted = false}, #diameter_packet{header = Hdr, msg = Msg}. @@ -2126,16 +2110,6 @@ answer_message({OH, OR, RC}, Avps) -> session_id(Code, Vid, Avps) when is_list(Avps) -> try - {value, #diameter_avp{} = Avp} = find_avp(Code, Vid, Avps), - Avp - catch - error: _ -> - [] - end; - -session_id(Code, Vid, Avps) - when is_list(Avps) -> - try {value, #diameter_avp{data = D}} = find_avp(Code, Vid, Avps), [{'Session-Id', [?BASE:avp(decode, D, 'Session-Id')]}] catch @@ -2482,6 +2456,7 @@ rpd(Pid, Alias, PDict) -> %%% %%% Output: {TransportPid, #diameter_caps{}, #diameter_app{}} %%% | false +%%% | {error, Reason} %%% --------------------------------------------------------------------------- %% Initial call, from an arbitrary process. @@ -2540,28 +2515,18 @@ get_destination(Msg, Dict) -> [str(get_avp_value(Dict, 'Destination-Realm', Msg)), str(get_avp_value(Dict, 'Destination-Host', Msg))]. -%% TODO: -%% -%% Should add some way of specifying destination directly so that the -%% only requirement is that the prepare_request callback returns -%% something specific. (eg. {host, DH}; that is, let the caller specify.) -%% -%% Also, there is no longer any need to call get_destination at all in -%% the default case. - -str(T) - when T == undefined; - T == [] -> +%% This is not entirely correct. The avp could have an arity 1, in +%% which case an empty list is a DiameterIdentity of length 0 rather +%% than the list of no values we treat it as by mapping to undefined. +%% This behaviour is documented. +str([]) -> undefined; -str([X]) - when is_list(X) -> - X; str(T) -> T. %% get_avp_value/3 %% -%% Support outgoing messages in one of three forms: +%% Find an AVP in a message of one of three forms: %% %% - a message record (as generated from a .dia spec) or %% - a list of an atom message name followed by 2-tuple, avp name/value pairs. @@ -2593,8 +2558,9 @@ get_avp_value(_, Name, [_MsgName | Avps]) -> undefined end; -get_avp_value(Dict, Name, Rec) - when is_tuple(Rec) -> +%% Message is typically a record but not necessarily: diameter:call/4 +%% can be passed an arbitrary term. +get_avp_value(Dict, Name, Rec) -> try Dict:'#get-'(Name, Rec) catch @@ -2690,7 +2656,8 @@ peers(Alias, RH, Filter, Peers) -> end. %% Place a peer whose Destination-Host/Realm matches those of the -%% request at the front of the result list. +%% request at the front of the result list. Could add some sort of +%% 'sort' option to allow more control. ps([], _, _, {Ys, Ns}) -> lists:reverse(Ys, Ns); @@ -2700,11 +2667,11 @@ ps([{_TPid, #diameter_caps{} = Caps} = TC | Rest], RH, Filter, Acc) -> TC, Acc)). -pacc(true, true, TC, {Ts, Fs}) -> - {[TC|Ts], Fs}; -pacc(true, false, TC, {Ts, Fs}) -> - {Ts, [TC|Fs]}; -pacc(false, _, _, Acc) -> +pacc(true, true, Peer, {Ts, Fs}) -> + {[Peer|Ts], Fs}; +pacc(true, false, Peer, {Ts, Fs}) -> + {Ts, [Peer|Fs]}; +pacc(_, _, _, Acc) -> Acc. %% caps_filter/3 @@ -2712,17 +2679,19 @@ pacc(false, _, _, Acc) -> caps_filter(C, RH, {neg, F}) -> not caps_filter(C, RH, F); -caps_filter(C, RH, {all, L}) -> +caps_filter(C, RH, {all, L}) + when is_list(L) -> lists:all(fun(F) -> caps_filter(C, RH, F) end, L); -caps_filter(C, RH, {any, L}) -> +caps_filter(C, RH, {any, L}) + when is_list(L) -> lists:any(fun(F) -> caps_filter(C, RH, F) end, L); -caps_filter(#diameter_caps{origin_host = {_,H}}, [_,DH], host) -> - eq(undefined, DH, H); +caps_filter(#diameter_caps{origin_host = {_,OH}}, [_,DH], host) -> + eq(undefined, DH, OH); -caps_filter(#diameter_caps{origin_realm = {_,R}}, [DR,_], realm) -> - eq(undefined, DR, R); +caps_filter(#diameter_caps{origin_realm = {_,OR}}, [DR,_], realm) -> + eq(undefined, DR, OR); caps_filter(C, _, Filter) -> caps_filter(C, Filter). @@ -2738,6 +2707,9 @@ caps_filter(#diameter_caps{origin_host = {_,OH}}, {host, H}) -> caps_filter(#diameter_caps{origin_realm = {_,OR}}, {realm, R}) -> eq(any, R, OR); +%% Anything else is expected to be an eval filter. Filter failure is +%% documented as being equivalent to a non-matching filter. + caps_filter(C, T) -> try {eval, F} = T, @@ -2746,8 +2718,14 @@ caps_filter(C, T) -> _:_ -> false end. -eq(X, A, B) -> - X == A orelse A == B. +eq(Any, Id, PeerId) -> + Any == Id orelse try + iolist_to_binary(Id) == iolist_to_binary(PeerId) + catch + _:_ -> false + end. +%% OctetString() can be specified as an iolist() so test for string +%% rather then term equality. %% transports/1 diff --git a/lib/diameter/src/app/diameter_stats.erl b/lib/diameter/src/app/diameter_stats.erl index b52d4cdcfb..71479afa95 100644 --- a/lib/diameter/src/app/diameter_stats.erl +++ b/lib/diameter/src/app/diameter_stats.erl @@ -207,7 +207,7 @@ handle_call({flush, Contrib}, _From, State) -> {reply, fetch(Contrib), State}; handle_call(Req, From, State) -> - warning_msg("received unexpected request from ~p:~n~w", [From, Req]), + ?UNEXPECTED([Req, From]), {reply, nok, State}. %% ---------------------------------------------------------- @@ -219,7 +219,7 @@ handle_cast({incr, Rec}, State) -> {noreply, State}; handle_cast(Msg, State) -> - warning_msg("received unexpected message:~n~w", [Msg]), + ?UNEXPECTED([Msg]), {noreply, State}. %% ---------------------------------------------------------- @@ -231,7 +231,7 @@ handle_info({'DOWN', _MRef, process, Pid, _}, State) -> {noreply, State}; handle_info(Info, State) -> - warning_msg("received unknown info:~n~w", [Info]), + ?UNEXPECTED([Info]), {noreply, State}. %% ---------------------------------------------------------- @@ -340,8 +340,3 @@ cast(Msg) -> call(Request) -> gen_server:call(?SERVER, Request, infinity). - -%% warning_msg/2 - -warning_msg(F, A) -> - ?diameter_warning("~p: " ++ F, [?MODULE | A]). diff --git a/lib/diameter/src/app/diameter_sync.erl b/lib/diameter/src/app/diameter_sync.erl index f7777ae809..ce2db4b3a2 100644 --- a/lib/diameter/src/app/diameter_sync.erl +++ b/lib/diameter/src/app/diameter_sync.erl @@ -204,37 +204,37 @@ handle_call(?REQUEST(Type, Name, Req, Max, Timeout), T = find(Name, QD), nq(queued(T) =< Max, T, {Type, From}, Name, Req, Timeout, State); -handle_call(Request, _From, State) -> - {reply, call(Request, State), State}. +handle_call(Request, From, State) -> + {reply, call(Request, From, State), State}. -%% call/2 +%% call/3 -call(?CARP(Name), #state{queue = QD}) -> +call(?CARP(Name), _, #state{queue = QD}) -> pcar(find(Name, QD)); -call(state, State) -> +call(state, _, State) -> State; -call(uptime, #state{time = T}) -> +call(uptime, _, #state{time = T}) -> diameter_lib:now_diff(T); -call({flush, Name}, #state{queue = QD}) -> +call({flush, Name}, _, #state{queue = QD}) -> cancel(find(Name, QD)); -call(pending, #state{pending = N}) -> +call(pending, _, #state{pending = N}) -> N; -call({pending, Name}, #state{queue = QD}) -> +call({pending, Name}, _, #state{queue = QD}) -> queued(find(Name, QD)); -call(queues, #state{queue = QD}) -> +call(queues, _, #state{queue = QD}) -> fetch_keys(QD); -call({pids, Name}, #state{queue = QD}) -> +call({pids, Name}, _, #state{queue = QD}) -> plist(find(Name, QD)); -call(Req, _State) -> %% ignore - warning_msg("received unexpected request:~n~w", [Req]), +call(Req, From, _State) -> %% ignore + ?UNEXPECTED(handle_call, [Req, From]), nok. %%% ---------------------------------------------------------- @@ -242,7 +242,7 @@ call(Req, _State) -> %% ignore %%% ---------------------------------------------------------- handle_cast(Msg, State) -> - warning_msg("received unexpected message:~n~w", [Msg]), + ?UNEXPECTED([Msg]), {noreply, State}. %%% ---------------------------------------------------------- @@ -267,7 +267,7 @@ info({'DOWN', MRef, process, Pid, Info}, queue = dq(fetch(Name, QD), Pid, Info, Name, QD)}; info(Info, State) -> - warning_msg("received unknown info:~n~w", [Info]), + ?UNEXPECTED(handle_info, [Info]), State. reply({call, From}, T) -> @@ -548,8 +548,3 @@ gen_call(Server, Req, Timeout) -> exit: _ -> timeout end. - -%% warning_msg/2 - -warning_msg(F, A) -> - ?diameter_warning("~p: " ++ F, [?MODULE | A]). diff --git a/lib/diameter/src/compiler/diameter_codegen.erl b/lib/diameter/src/compiler/diameter_codegen.erl index 30caebc544..a33b07a3d3 100644 --- a/lib/diameter/src/compiler/diameter_codegen.erl +++ b/lib/diameter/src/compiler/diameter_codegen.erl @@ -37,7 +37,6 @@ file/2, file/3]). --include_lib("diameter/src/app/diameter_internal.hrl"). -include("diameter_forms.hrl"). %% Generated functions that could have no generated clauses will have |