aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter
diff options
context:
space:
mode:
authorAnders Svensson <anders@erlang.org>2013-05-31 14:46:59 +0200
committerAnders Svensson <anders@erlang.org>2013-06-02 15:00:19 +0200
commit21e778b998b895034453251d83c3e6aaa72fe395 (patch)
tree1157816c06423b0eed15310537e77c58aafe161b /lib/diameter
parent22685099ace9802016bf6203c525702084717d72 (diff)
downloadotp-21e778b998b895034453251d83c3e6aaa72fe395.tar.gz
otp-21e778b998b895034453251d83c3e6aaa72fe395.tar.bz2
otp-21e778b998b895034453251d83c3e6aaa72fe395.zip
Fix setting of Failed-AVP on {answer_message, 5xxx} from handle_request
RFC 6733 says that certain 5xxx result codes must be accompanied by Failed-AVP, and decode populates #diameter_packet.errors with Result-Code/AVP pairs for errors it detects. However, Failed-AVP was not set in the outgoing answer if the handle_request callback returned {answer_message, 5xxx}. It is now set with the AVP from the first pair with the specified Result-Code, if found. Note that {answer_message, 5xxx} doesn't handle all cases in which a 5xxx answer is required, only that in which the setting above is appropriate. If it isn't then handle_request should construct its answer and return {reply, Ans}.
Diffstat (limited to 'lib/diameter')
-rw-r--r--lib/diameter/doc/src/diameter_app.xml14
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl22
-rw-r--r--lib/diameter/test/diameter_3xxx_SUITE.erl36
3 files changed, 61 insertions, 11 deletions
diff --git a/lib/diameter/doc/src/diameter_app.xml b/lib/diameter/doc/src/diameter_app.xml
index d4fb792787..e6c9cc9a90 100644
--- a/lib/diameter/doc/src/diameter_app.xml
+++ b/lib/diameter/doc/src/diameter_app.xml
@@ -565,7 +565,8 @@ Equivalent to</p>
</pre>
<p>
where <c>Avps</c> sets the Origin-Host, Origin-Realm, the specified
-Result-Code and (if the request contained one) Session-Id AVP's.</p>
+Result-Code and (if the request contained one) Session-Id AVP's, and
+possibly Failed-AVP as described below.</p>
<p>
Returning a value other than 3xxx or 5xxx will cause the request
@@ -573,6 +574,14 @@ process in question to fail, as will returning a 5xxx value if the
peer connection in question has been configured with the RFC 3588
common dictionary <c>diameter_gen_base_rfc3588</c>.
(Since RFC 3588 only allows 3xxx values in an answer-message.)</p>
+
+<p>
+When returning 5xxx, Failed-AVP will be populated with the AVP of the
+first matching Result-Code/AVP pair in the <c>errors</c> field of the
+argument &packet;, if found.
+If this is not appropriate then an answer-message should be
+constructed explicitly and returned in a <c>reply</c> tuple
+instead.</p>
</item>
<tag><c>{relay, Opts}</c></tag>
@@ -592,8 +601,7 @@ header of the relayed request.</p>
The returned <c>Opts</c> should not specify <c>detach</c>.
A subsequent &handle_answer;
callback for the relayed request must return its first
-argument, the <c>#diameter_packet{}</c> record containing the answer
-message.
+argument, the &packet; 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.
Any other return value (for example, from a
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 820d37535a..0b15e68ec7 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -479,10 +479,9 @@ answer_message(RC,
#diameter_caps{origin_host = {OH,_},
origin_realm = {OR,_}},
Dict0,
- #diameter_packet{avps = Avps}
- = Pkt) ->
+ Pkt) ->
?LOG({error, RC}, Pkt),
- {Dict0, answer_message(OH, OR, RC, Dict0, Avps)}.
+ {Dict0, answer_message(OH, OR, RC, Dict0, Pkt)}.
%% resend/7
@@ -861,12 +860,14 @@ failed(Rec, FailedAvp, Dict) ->
%% answer_message/5
-answer_message(OH, OR, RC, Dict0, Avps) ->
+answer_message(OH, OR, RC, Dict0, #diameter_packet{avps = Avps,
+ errors = Es}) ->
{Code, _, Vid} = Dict0:avp_header('Session-Id'),
['answer-message', {'Origin-Host', OH},
{'Origin-Realm', OR},
- {'Result-Code', RC}
- | session_id(Code, Vid, Dict0, Avps)].
+ {'Result-Code', RC}]
+ ++ session_id(Code, Vid, Dict0, Avps)
+ ++ failed_avp(RC, Es).
session_id(Code, Vid, Dict0, Avps)
when is_list(Avps) ->
@@ -878,6 +879,15 @@ session_id(Code, Vid, Dict0, Avps)
[]
end.
+%% Note that this should only match 5xxx result codes currently but
+%% don't bother distinguishing this case.
+failed_avp(RC, [{RC, Avp} | _]) ->
+ [{'Failed-AVP', [{'AVP', [Avp]}]}];
+failed_avp(RC, [_ | Es]) ->
+ failed_avp(RC, Es);
+failed_avp(_, [] = No) ->
+ No.
+
%% find_avp/3
find_avp(Code, Vid, Avps)
diff --git a/lib/diameter/test/diameter_3xxx_SUITE.erl b/lib/diameter/test/diameter_3xxx_SUITE.erl
index 0ec0d5020f..071b1a1177 100644
--- a/lib/diameter/test/diameter_3xxx_SUITE.erl
+++ b/lib/diameter/test/diameter_3xxx_SUITE.erl
@@ -43,6 +43,7 @@
send_invalid_hdr_bits/1,
send_missing_avp/1,
send_ignore_missing_avp/1,
+ send_5xxx_missing_avp/1,
send_double_error/1,
send_3xxx/1,
send_5xxx/1,
@@ -139,6 +140,7 @@ tc() ->
send_invalid_hdr_bits,
send_missing_avp,
send_ignore_missing_avp,
+ send_5xxx_missing_avp,
send_double_error,
send_3xxx,
send_5xxx].
@@ -279,6 +281,32 @@ send_ignore_missing_avp([_,_]) ->
send_ignore_missing_avp(Config) ->
send_ignore_missing_avp(?group(Config)).
+%% send_5xxx_missing_avp/1
+%%
+%% Send a request with a missing AVP that a callback answers
+%% with {answer_message, 5005}.
+
+%% RFC 6733 allows 5xxx in an answer-message.
+send_5xxx_missing_avp([_, rfc6733]) ->
+ #'diameter_base_answer-message'{'Result-Code' = 5005, %% MISSING_AVP
+ 'Failed-AVP' = [_],
+ 'AVP' = []}
+ = call();
+
+%% RFC 3588 doesn't: sending answer fails.
+send_5xxx_missing_avp([_, rfc3588]) ->
+ {error, timeout} = call();
+
+%% Callback answers, ignores the error
+send_5xxx_missing_avp([_,_]) ->
+ #diameter_base_STA{'Result-Code' = 2001, %% SUCCESS
+ 'Failed-AVP' = [],
+ 'AVP' = []}
+ = call();
+
+send_5xxx_missing_avp(Config) ->
+ send_5xxx_missing_avp(?group(Config)).
+
%% send_double_error/1
%%
%% Send a request with both an invalid E-bit and a missing AVP.
@@ -403,7 +431,8 @@ prepare(Pkt0, Caps, send_double_error) ->
prepare(Pkt, Caps, T)
when T == send_missing_avp;
- T == send_ignore_missing_avp ->
+ T == send_ignore_missing_avp;
+ T == send_5xxx_missing_avp ->
Req = sta(Pkt, Caps),
dehost(diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req})).
@@ -487,7 +516,10 @@ request(T, Req, Caps)
request(send_ignore_missing_avp, Req, Caps) ->
{reply, #diameter_packet{msg = answer(Req, Caps),
- errors = false}}. %% ignore errors
+ errors = false}}; %% ignore errors
+
+request(send_5xxx_missing_avp, _Req, _Caps) ->
+ {answer_message, 5005}. %% MISSING_AVP
answer(Req, Caps) ->
#diameter_base_STR{'Session-Id' = SId}