aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/src
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2013-02-11 00:04:37 +0100
committerAnders Svensson <[email protected]>2013-02-16 18:34:42 +0100
commitc9ca7f7e836dc6a4fe91fd4d7103af6d4db76f0e (patch)
treec9e26b79b00e5a6df5bd2567674dd8f7ffefcc91 /lib/diameter/src
parentbbf692965470a9e993e1afd6f1a9375cbe832fcb (diff)
downloadotp-c9ca7f7e836dc6a4fe91fd4d7103af6d4db76f0e.tar.gz
otp-c9ca7f7e836dc6a4fe91fd4d7103af6d4db76f0e.tar.bz2
otp-c9ca7f7e836dc6a4fe91fd4d7103af6d4db76f0e.zip
Add application_opt() request_errors
Configuring the value 'callback' all errors detected in incoming requests to result in a handle_request callback. The default value 'answer_3xxx' is the previous behaviour in which diameter answers protocol errors without a callback.
Diffstat (limited to 'lib/diameter/src')
-rw-r--r--lib/diameter/src/base/diameter.erl3
-rw-r--r--lib/diameter/src/base/diameter_config.erl11
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl205
3 files changed, 116 insertions, 103 deletions
diff --git a/lib/diameter/src/base/diameter.erl b/lib/diameter/src/base/diameter.erl
index f563d244f6..7359688404 100644
--- a/lib/diameter/src/base/diameter.erl
+++ b/lib/diameter/src/base/diameter.erl
@@ -306,7 +306,8 @@ call(SvcName, App, Message) ->
| {module, app_module()}
| {state, any()}
| {call_mutates_state, boolean()}
- | {answer_errors, callback|report|discard}.
+ | {answer_errors, callback|report|discard}
+ | {request_errors, callback|answer_3xxx}.
-type app_alias()
:: any().
diff --git a/lib/diameter/src/base/diameter_config.erl b/lib/diameter/src/base/diameter_config.erl
index 1486071573..889c75e3da 100644
--- a/lib/diameter/src/base/diameter_config.erl
+++ b/lib/diameter/src/base/diameter_config.erl
@@ -672,13 +672,15 @@ app_acc({application, Opts}, Acc) ->
ModS = get_opt(state, Opts, Alias),
M = get_opt(call_mutates_state, Opts, false),
A = get_opt(answer_errors, Opts, report),
+ R = get_opt(request_errors, Opts, answer_3xxx),
[#diameter_app{alias = Alias,
dictionary = Dict,
id = cb(Dict, id),
module = init_mod(Mod),
init_state = ModS,
mutable = init_mutable(M),
- options = [{answer_errors, init_answers(A)}]}
+ options = [{answer_errors, init_answers(A)},
+ {request_errors, init_request_errors(R)}]}
| Acc];
app_acc(_, Acc) ->
Acc.
@@ -722,6 +724,13 @@ init_answers(A)
init_answers(A) ->
?THROW({answer_errors, A}).
+init_request_errors(P)
+ when callback == P;
+ answer_3xxx == P ->
+ P;
+init_request_errors(P) ->
+ ?THROW({request_errors, P}).
+
%% Get a single value at the specified key.
get_opt(Keys, List)
when is_list(Keys) ->
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 0de3825943..c3b9c195fb 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -205,16 +205,25 @@ recv_request({#diameter_app{id = Id, dictionary = Dict} = App, Caps},
Caps,
Dict0,
RecvData,
- diameter_codec:decode(Id, Dict, Pkt));
+ errors(Id, diameter_codec:decode(Id, Dict, Pkt)));
%% Note that the decode is different depending on whether or not Id is
%% ?APP_ID_RELAY.
%% DIAMETER_APPLICATION_UNSUPPORTED 3007
%% A request was sent for an application that is not supported.
-recv_request(#diameter_caps{} = Caps, TPid, Pkt, Dict0, _) ->
- As = collect_avps(Pkt),
- protocol_error(3007, TPid, Caps, Dict0, Pkt#diameter_packet{avps = As});
+recv_request(#diameter_caps{}
+ = Caps,
+ TPid,
+ #diameter_packet{errors = Es}
+ = Pkt,
+ Dict0,
+ _) ->
+ protocol_error(TPid,
+ Caps,
+ Dict0,
+ Pkt#diameter_packet{avps = collect_avps(Pkt),
+ errors = [3007 | Es]});
recv_request(false, _, _, _, _) -> %% transport has gone down
ok.
@@ -229,75 +238,25 @@ collect_avps(Pkt) ->
%% recv_R/6
-%% Wrong number of bits somewhere in the message: reply.
-%%
-%% 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.
-%%
-recv_R(_App,
+%% Answer 3xxx errors ourselves ...
+recv_R(#diameter_app{options = [_, {request_errors, answer_3xxx} | _]},
TPid,
Caps,
Dict0,
_RecvData,
- #diameter_packet{errors = [Bs | _]} = Pkt)
- when is_bitstring(Bs) ->
- protocol_error(3009, TPid, Caps, Dict0, Pkt);
+ #diameter_packet{errors = [RC|_]} = Pkt)
+ when 3 == RC div 1000 ->
+ protocol_error(TPid, Caps, Dict0, Pkt);
-%% Either we support this application but don't recognize the command
-%% or we're a relay and the command isn't proxiable.
-%%
-%% 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
-%% receives an experimental command that it does not understand.
-%%
-recv_R(#diameter_app{id = Id},
+%% ... or make a handle_request callback. Note that
+%% Pkt#diameter_packet.msg = undefined in the 3001 case.
+recv_R(App,
TPid,
Caps,
Dict0,
- _RecvData,
- #diameter_packet{header = #diameter_header{is_proxiable = P},
- msg = M}
- = Pkt)
- when ?APP_ID_RELAY /= Id, undefined == M;
- ?APP_ID_RELAY == Id, not P ->
- protocol_error(3001, TPid, Caps, Dict0, Pkt);
-
-%% Error bit was set on a request.
-%%
-%% DIAMETER_INVALID_HDR_BITS 3008
-%% A request was received whose bits in the Diameter header were
-%% either set to an invalid combination, or to a value that is
-%% inconsistent with the command code's definition.
-%%
-recv_R(_App,
- TPid,
- Caps,
- Dict0,
- _RecvData,
- #diameter_packet{header = #diameter_header{is_error = true}}
- = Pkt) ->
- protocol_error(3008, TPid, Caps, Dict0, Pkt);
-
-%% A message in a locally supported application or a proxiable message
-%% in the relay application. Don't distinguish between the two since
-%% each application has its own callback config. That is, the user can
-%% easily distinguish between the two cases.
-recv_R(App, TPid, Caps, Dict0, RecvData, Pkt) ->
- request_cb(App, TPid, Caps, Dict0, RecvData, examine(Pkt)).
-
-%% Note that there may still be errors but these aren't protocol
-%% (3xxx) errors that lead to an answer-message.
-
-request_cb(App,
- TPid,
- Caps,
- Dict0,
- #recvdata{service_name = SvcName}
- = RecvData,
- Pkt) ->
+ #recvdata{service_name = SvcName}
+ = RecvData,
+ Pkt) ->
request_cb(cb(App, handle_request, [Pkt, SvcName, {TPid, Caps}]),
App,
TPid,
@@ -307,20 +266,21 @@ request_cb(App,
[],
Pkt).
-%% examine/1
+%% errors/1
%%
-%% Look for errors in a decoded message. It's odd/unfortunate that
-%% 501[15] aren't protocol errors.
+%% Look for errors in a decoded message, prepending the errors field
+%% with the first detected error. It's odd/unfortunate that 5011 isn't
+%% a protocol error.
%% DIAMETER_INVALID_MESSAGE_LENGTH 5015
%%
%% This error is returned when a request is received with an invalid
%% message length.
-examine(#diameter_packet{header = #diameter_header{length = Len},
- bin = Bin,
- errors = Es}
- = Pkt)
+errors(_, #diameter_packet{header = #diameter_header{length = Len},
+ bin = Bin,
+ errors = Es}
+ = Pkt)
when Len < 20;
0 /= Len rem 4;
8*Len /= bit_size(Bin) ->
@@ -330,13 +290,47 @@ examine(#diameter_packet{header = #diameter_header{length = Len},
%% This error is returned when a request was received, whose version
%% number is unsupported.
-examine(#diameter_packet{header = #diameter_header{version = V},
- errors = Es}
- = Pkt)
+errors(_, #diameter_packet{header = #diameter_header{version = V},
+ errors = Es}
+ = Pkt)
when V /= ?DIAMETER_VERSION ->
Pkt#diameter_packet{errors = [5011 | Es]};
-examine(Pkt) ->
+%% 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) ->
+ 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
+%% receives an experimental command that it does not understand.
+
+errors(Id, #diameter_packet{header = #diameter_header{is_proxiable = P},
+ msg = M,
+ errors = Es}
+ = Pkt)
+ when ?APP_ID_RELAY /= Id, undefined == M; %% don't know the command
+ ?APP_ID_RELAY == Id, not P -> %% command isn't proxiable
+ Pkt#diameter_packet{errors = [3001 | Es]};
+
+%% DIAMETER_INVALID_HDR_BITS 3008
+%% A request was received whose bits in the Diameter header were
+%% either set to an invalid combination, or to a value that is
+%% inconsistent with the command code's definition.
+
+errors(_, #diameter_packet{header = #diameter_header{is_request = true,
+ is_error = true},
+ errors = Es}
+ = Pkt) ->
+ Pkt#diameter_packet{errors = [3008 | Es]};
+
+%% Green.
+errors(_, Pkt) ->
Pkt.
%% request_cb/8
@@ -365,7 +359,7 @@ request_cb({protocol_error, RC},
_RecvData,
Fs,
Pkt)
- when 3000 =< RC, RC < 4000 ->
+ when 3 == RC div 1000 ->
protocol_error(RC, TPid, Caps, Dict0, Fs, Pkt);
%% RFC 3588 says we must reply 3001 to anything unrecognized or
@@ -447,28 +441,36 @@ dict(Dict, Dict0, Rec) ->
error:_ -> Dict
end.
-%% protocol_error/6
-
-protocol_error(RC, TPid, Caps, Dict0, Fs, Pkt) ->
- #diameter_caps{origin_host = {OH,_},
- origin_realm = {OR,_}}
- = Caps,
- #diameter_packet{avps = Avps, errors = Es}
- = Pkt,
+%% protocol_error/5
+protocol_error(TPid,
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}},
+ Dict0,
+ Fs,
+ #diameter_packet{avps = Avps,
+ errors = [RC|_]}
+ = Pkt) ->
?LOG({error, RC}, Pkt),
- reply(answer_message({OH, OR, RC}, Dict0, Avps),
- Dict0,
- TPid,
- Fs,
- Pkt#diameter_packet{errors = [RC | Es]}).
+ reply(answer_message({OH, OR, RC}, Dict0, Avps), Dict0, TPid, Fs, Pkt).
%% Note that reply/5 may set the result code once more. It's set in
%% answer_message/3 in case reply/5 doesn't.
-%% protocol_error/5
+protocol_error(TPid, Caps, Dict0, Pkt) ->
+ protocol_error(TPid, Caps, Dict0, [], Pkt).
-protocol_error(RC, TPid, Caps, Dict0, Pkt) ->
- protocol_error(RC, TPid, Caps, Dict0, [], Pkt).
+protocol_error(RC,
+ TPid,
+ Caps,
+ Dict0,
+ Fs,
+ #diameter_packet{errors = Es}
+ = Pkt) ->
+ protocol_error(TPid,
+ Caps,
+ Dict0,
+ Fs,
+ Pkt#diameter_packet{errors = [RC | Es]}).
%% resend/7
%%
@@ -661,15 +663,15 @@ rc(RC) ->
%% rc/4
-rc(#diameter_packet{msg = Rec} = Pkt, RC, Failed, DictT) ->
- Pkt#diameter_packet{msg = rc(Rec, RC, Failed, DictT)};
+rc(#diameter_packet{msg = Rec} = Pkt, RC, Failed, Dict) ->
+ Pkt#diameter_packet{msg = rc(Rec, RC, Failed, Dict)};
-rc(Rec, RC, Failed, DictT)
+rc(Rec, RC, Failed, Dict)
when is_integer(RC) ->
set(Rec,
- lists:append([rc(Rec, {'Result-Code', RC}, DictT),
- failed_avp(Rec, Failed, DictT)]),
- DictT).
+ lists:append([rc(Rec, {'Result-Code', RC}, Dict),
+ failed_avp(Rec, Failed, Dict)]),
+ Dict).
%% Reply as name and tuple list ...
set([_|_] = Ans, Avps, _) ->
@@ -1268,11 +1270,12 @@ handle_answer(SvcName, App, {error, Req, Reason}) ->
handle_error(App, Req, Reason, SvcName);
handle_answer(SvcName,
- #diameter_app{dictionary = Dict}
+ #diameter_app{dictionary = Dict,
+ id = Id}
= App,
{answer, Req, Dict0, Pkt}) ->
Mod = dict(Dict, Dict0, Pkt),
- answer(examine(diameter_codec:decode(Mod, Pkt)),
+ answer(errors(Id, diameter_codec:decode(Mod, Pkt)),
SvcName,
Mod,
App,