1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2013-2016. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20
21%%
22%% Tests of application_opt() request_errors. There's some overlap
23%% between this suite and the traffic suite but latter exercises more
24%% config.
25%%
26
27-module(diameter_3xxx_SUITE).
28
29-export([suite/0,
30         all/0,
31         groups/0,
32         init_per_suite/1,
33         end_per_suite/1,
34         init_per_group/2,
35         end_per_group/2,
36         init_per_testcase/2,
37         end_per_testcase/2]).
38
39%% testcases
40-export([start/1,
41         send_unknown_application/1,
42         send_unknown_command/1,
43         send_ok/1,
44         send_invalid_hdr_bits/1,
45         send_missing_avp/1,
46         send_ignore_missing_avp/1,
47         send_5xxx_missing_avp/1,
48         send_double_error/1,
49         send_3xxx/1,
50         send_5xxx/1,
51         counters/1,
52         stop/1]).
53
54%% diameter callbacks
55-export([peer_up/3,
56         peer_down/3,
57         pick_peer/5,
58         prepare_request/4,
59         handle_answer/5,
60         handle_error/5,
61         handle_request/3]).
62
63-include("diameter.hrl").
64-include("diameter_gen_base_rfc6733.hrl").
65%% Use the fact that STR/STA is identical in RFC's 3588 and 6733.
66
67%% ===========================================================================
68
69-define(util, diameter_util).
70-define(testcase(), proplists:get_value(testcase, get(?MODULE))).
71-define(group(Config), begin
72                           put(?MODULE, Config),
73                           ?util:name(proplists:get_value(group, Config))
74                       end).
75
76-define(L, atom_to_list).
77-define(A, list_to_atom).
78
79-define(CLIENT, "CLIENT").
80-define(SERVER, "SERVER").
81-define(REALM, "erlang.org").
82-define(HOST(Host, Realm), Host ++ [$.|Realm]).
83
84-define(ERRORS, [answer, answer_3xxx, callback]).
85-define(RFCS, [rfc3588, rfc6733]).
86-define(DICT(RFC), ?A("diameter_gen_base_" ++ ?L(RFC))).
87-define(DICT, ?DICT(rfc6733)).
88
89-define(COMMON, ?DIAMETER_APP_ID_COMMON).
90
91%% Config for diameter:start_service/2.
92-define(SERVICE(Name, Errors, RFC),
93        [{'Origin-Host', Name ++ "." ++ ?REALM},
94         {'Origin-Realm', ?REALM},
95         {'Host-IP-Address', [{127,0,0,1}]},
96         {'Vendor-Id', 12345},
97         {'Product-Name', "OTP/diameter"},
98         {'Auth-Application-Id', [?COMMON]},
99         {application, [{dictionary, ?DICT(RFC)},
100                        {module, ?MODULE},
101                        {answer_errors, callback},
102                        {request_errors, Errors}]}]).
103
104-define(LOGOUT, ?'DIAMETER_BASE_TERMINATION-CAUSE_LOGOUT').
105
106%% ===========================================================================
107
108suite() ->
109    [{timetrap, {seconds, 60}}].
110
111all() ->
112    [{group, ?util:name([E,D])} || E <- ?ERRORS, D <- ?RFCS].
113
114groups() ->
115    Tc = tc(),
116    [{?util:name([E,D]), [], [start] ++ Tc ++ [counters, stop]}
117     || E <- ?ERRORS, D <- ?RFCS].
118
119init_per_suite(Config) ->
120    ok = diameter:start(),
121    Config.
122
123end_per_suite(_Config) ->
124    ok = diameter:stop().
125
126init_per_group(Group, Config) ->
127    [{group, Group} | Config].
128
129end_per_group(_, _) ->
130    ok.
131
132init_per_testcase(Name, Config) ->
133    [{testcase, Name} | Config].
134
135end_per_testcase(_, _) ->
136    ok.
137
138tc() ->
139    [send_unknown_application,
140     send_unknown_command,
141     send_ok,
142     send_invalid_hdr_bits,
143     send_missing_avp,
144     send_ignore_missing_avp,
145     send_5xxx_missing_avp,
146     send_double_error,
147     send_3xxx,
148     send_5xxx].
149
150%% ===========================================================================
151
152%% start/1
153
154start(Config) ->
155    Group = proplists:get_value(group, Config),
156    [Errors, RFC] = ?util:name(Group),
157    ok = diameter:start_service(?SERVER, ?SERVICE(?L(Group),
158                                                  Errors,
159                                                  RFC)),
160    ok = diameter:start_service(?CLIENT, ?SERVICE(?CLIENT,
161                                                  callback,
162                                                  rfc6733)),
163    LRef = ?util:listen(?SERVER, tcp),
164    ?util:connect(?CLIENT, tcp, LRef).
165
166%% stop/1
167
168stop(_Config) ->
169    ok = diameter:remove_transport(?CLIENT, true),
170    ok = diameter:remove_transport(?SERVER, true),
171    ok = diameter:stop_service(?SERVER),
172    ok = diameter:stop_service(?CLIENT).
173
174%% counters/1
175%%
176%% Check that counters are as expected.
177
178counters(Config) ->
179    Group = proplists:get_value(group, Config),
180    [_Errors, _Rfc] = G = ?util:name(Group),
181    [] = ?util:run([[fun counters/3, K, S, G]
182                    || K <- [statistics, transport, connections],
183                       S <- [?CLIENT, ?SERVER]]).
184
185counters(Key, Svc, Group) ->
186    counters(Key, Svc, Group, [_|_] = diameter:service_info(Svc, Key)).
187
188counters(statistics, Svc, [Errors, Rfc], L) ->
189    [{P, Stats}] = L,
190    true = is_pid(P),
191    stats(Svc, Errors, Rfc, lists:sort(Stats));
192
193counters(_, _, _, _) ->
194    todo.
195
196stats(?CLIENT, E, rfc3588, L)
197  when E == answer;
198       E == answer_3xxx ->
199    [{{{unknown,0},recv},2},
200     {{{0,257,0},recv},1},
201     {{{0,257,1},send},1},
202     {{{0,275,0},recv},6},
203     {{{0,275,1},send},10},
204     {{{unknown,0},recv,{'Result-Code',3001}},1},
205     {{{unknown,0},recv,{'Result-Code',3007}},1},
206     {{{0,257,0},recv,{'Result-Code',2001}},1},
207     {{{0,275,0},recv,{'Result-Code',2001}},1},
208     {{{0,275,0},recv,{'Result-Code',3008}},2},
209     {{{0,275,0},recv,{'Result-Code',3999}},1},
210     {{{0,275,0},recv,{'Result-Code',5002}},1},
211     {{{0,275,0},recv,{'Result-Code',5005}},1}]
212        = L;
213
214stats(?SERVER, E, rfc3588, L)
215  when E == answer;
216       E == answer_3xxx ->
217    [{{{unknown,0},send},2},
218     {{{unknown,1},recv},1},
219     {{{0,257,0},send},1},
220     {{{0,257,1},recv},1},
221     {{{0,275,0},send},6},
222     {{{0,275,1},recv},8},
223     {{{unknown,0},send,{'Result-Code',3001}},1},
224     {{{unknown,0},send,{'Result-Code',3007}},1},
225     {{{unknown,1},recv,error},1},
226     {{{0,257,0},send,{'Result-Code',2001}},1},
227     {{{0,275,0},send,{'Result-Code',2001}},1},
228     {{{0,275,0},send,{'Result-Code',3008}},2},
229     {{{0,275,0},send,{'Result-Code',3999}},1},
230     {{{0,275,0},send,{'Result-Code',5002}},1},
231     {{{0,275,0},send,{'Result-Code',5005}},1},
232     {{{0,275,1},recv,error},5}]
233        = L;
234
235stats(?CLIENT, answer, rfc6733, L) ->
236    [{{{unknown,0},recv},2},
237     {{{0,257,0},recv},1},
238     {{{0,257,1},send},1},
239     {{{0,275,0},recv},8},
240     {{{0,275,1},send},10},
241     {{{unknown,0},recv,{'Result-Code',3001}},1},
242     {{{unknown,0},recv,{'Result-Code',3007}},1},
243     {{{0,257,0},recv,{'Result-Code',2001}},1},
244     {{{0,275,0},recv,{'Result-Code',3008}},2},
245     {{{0,275,0},recv,{'Result-Code',3999}},1},
246     {{{0,275,0},recv,{'Result-Code',5002}},1},
247     {{{0,275,0},recv,{'Result-Code',5005}},3},
248     {{{0,275,0},recv,{'Result-Code',5999}},1}]
249        = L;
250
251stats(?SERVER, answer, rfc6733, L) ->
252    [{{{unknown,0},send},2},
253     {{{unknown,1},recv},1},
254     {{{0,257,0},send},1},
255     {{{0,257,1},recv},1},
256     {{{0,275,0},send},8},
257     {{{0,275,1},recv},8},
258     {{{unknown,0},send,{'Result-Code',3001}},1},
259     {{{unknown,0},send,{'Result-Code',3007}},1},
260     {{{unknown,1},recv,error},1},
261     {{{0,257,0},send,{'Result-Code',2001}},1},
262     {{{0,275,0},send,{'Result-Code',3008}},2},
263     {{{0,275,0},send,{'Result-Code',3999}},1},
264     {{{0,275,0},send,{'Result-Code',5002}},1},
265     {{{0,275,0},send,{'Result-Code',5005}},3},
266     {{{0,275,0},send,{'Result-Code',5999}},1},
267     {{{0,275,1},recv,error},5}]
268        = L;
269
270stats(?CLIENT, answer_3xxx, rfc6733, L) ->
271    [{{{unknown,0},recv},2},
272     {{{0,257,0},recv},1},
273     {{{0,257,1},send},1},
274     {{{0,275,0},recv},8},
275     {{{0,275,1},send},10},
276     {{{unknown,0},recv,{'Result-Code',3001}},1},
277     {{{unknown,0},recv,{'Result-Code',3007}},1},
278     {{{0,257,0},recv,{'Result-Code',2001}},1},
279     {{{0,275,0},recv,{'Result-Code',2001}},1},
280     {{{0,275,0},recv,{'Result-Code',3008}},2},
281     {{{0,275,0},recv,{'Result-Code',3999}},1},
282     {{{0,275,0},recv,{'Result-Code',5002}},1},
283     {{{0,275,0},recv,{'Result-Code',5005}},2},
284     {{{0,275,0},recv,{'Result-Code',5999}},1}]
285        = L;
286
287stats(?SERVER, answer_3xxx, rfc6733, L) ->
288    [{{{unknown,0},send},2},
289     {{{unknown,1},recv},1},
290     {{{0,257,0},send},1},
291     {{{0,257,1},recv},1},
292     {{{0,275,0},send},8},
293     {{{0,275,1},recv},8},
294     {{{unknown,0},send,{'Result-Code',3001}},1},
295     {{{unknown,0},send,{'Result-Code',3007}},1},
296     {{{unknown,1},recv,error},1},
297     {{{0,257,0},send,{'Result-Code',2001}},1},
298     {{{0,275,0},send,{'Result-Code',2001}},1},
299     {{{0,275,0},send,{'Result-Code',3008}},2},
300     {{{0,275,0},send,{'Result-Code',3999}},1},
301     {{{0,275,0},send,{'Result-Code',5002}},1},
302     {{{0,275,0},send,{'Result-Code',5005}},2},
303     {{{0,275,0},send,{'Result-Code',5999}},1},
304     {{{0,275,1},recv,error},5}]
305        = L;
306
307stats(?CLIENT, callback, rfc3588, L) ->
308    [{{{unknown,0},recv},1},
309     {{{0,257,0},recv},1},
310     {{{0,257,1},send},1},
311     {{{0,275,0},recv},6},
312     {{{0,275,1},send},10},
313     {{{unknown,0},recv,{'Result-Code',3007}},1},
314     {{{0,257,0},recv,{'Result-Code',2001}},1},
315     {{{0,275,0},recv,{'Result-Code',2001}},2},
316     {{{0,275,0},recv,{'Result-Code',3999}},1},
317     {{{0,275,0},recv,{'Result-Code',5002}},1},
318     {{{0,275,0},recv,{'Result-Code',5005}},2}]
319        = L;
320
321stats(?SERVER, callback, rfc3588, L) ->
322    [{{{unknown,0},send},1},
323     {{{unknown,1},recv},1},
324     {{{0,257,0},send},1},
325     {{{0,257,1},recv},1},
326     {{{0,275,0},send},6},
327     {{{0,275,1},recv},8},
328     {{{unknown,0},send,{'Result-Code',3007}},1},
329     {{{unknown,1},recv,error},1},
330     {{{0,257,0},send,{'Result-Code',2001}},1},
331     {{{0,275,0},send,{'Result-Code',2001}},2},
332     {{{0,275,0},send,{'Result-Code',3999}},1},
333     {{{0,275,0},send,{'Result-Code',5002}},1},
334     {{{0,275,0},send,{'Result-Code',5005}},2},
335     {{{0,275,1},recv,error},5}]
336        = L;
337
338stats(?CLIENT, callback, rfc6733, L) ->
339    [{{{unknown,0},recv},1},
340     {{{0,257,0},recv},1},
341     {{{0,257,1},send},1},
342     {{{0,275,0},recv},8},
343     {{{0,275,1},send},10},
344     {{{unknown,0},recv,{'Result-Code',3007}},1},
345     {{{0,257,0},recv,{'Result-Code',2001}},1},
346     {{{0,275,0},recv,{'Result-Code',2001}},2},
347     {{{0,275,0},recv,{'Result-Code',3999}},1},
348     {{{0,275,0},recv,{'Result-Code',5002}},1},
349     {{{0,275,0},recv,{'Result-Code',5005}},3},
350     {{{0,275,0},recv,{'Result-Code',5999}},1}]
351        = L;
352
353stats(?SERVER, callback, rfc6733, L) ->
354    [{{{unknown,0},send},1},
355     {{{unknown,1},recv},1},
356     {{{0,257,0},send},1},
357     {{{0,257,1},recv},1},
358     {{{0,275,0},send},8},
359     {{{0,275,1},recv},8},
360     {{{unknown,0},send,{'Result-Code',3007}},1},
361     {{{unknown,1},recv,error},1},
362     {{{0,257,0},send,{'Result-Code',2001}},1},
363     {{{0,275,0},send,{'Result-Code',2001}},2},
364     {{{0,275,0},send,{'Result-Code',3999}},1},
365     {{{0,275,0},send,{'Result-Code',5002}},1},
366     {{{0,275,0},send,{'Result-Code',5005}},3},
367     {{{0,275,0},send,{'Result-Code',5999}},1},
368     {{{0,275,1},recv,error},5}]
369        = L.
370
371%% send_unknown_application/1
372%%
373%% Send an unknown application that a callback (which shouldn't take
374%% place) fails on.
375
376%% diameter answers.
377send_unknown_application([_,_]) ->
378    #'diameter_base_answer-message'{'Result-Code' = 3007,
379                                                 %% UNSUPPORTED_APPLICATION
380                                    'Failed-AVP' = [],
381                                    'AVP' = []}
382        = call();
383
384send_unknown_application(Config) ->
385    send_unknown_application(?group(Config)).
386
387%% send_unknown_command/1
388%%
389%% Send a unknown command that a callback discards.
390
391%% handle_request discards the request.
392send_unknown_command([callback, _]) ->
393    {error, timeout} = call();
394
395%% diameter answers.
396send_unknown_command([_,_]) ->
397    #'diameter_base_answer-message'{'Result-Code' = 3001,
398                                                 %% UNSUPPORTED_COMMAND
399                                    'Failed-AVP' = [],
400                                    'AVP' = []}
401        = call();
402
403send_unknown_command(Config) ->
404    send_unknown_command(?group(Config)).
405
406%% send_ok/1
407%%
408%% Send a correct STR that a callback answers with 5002.
409
410%% Callback answers.
411send_ok([_,_]) ->
412    #diameter_base_STA{'Result-Code' = 5002,  %% UNKNOWN_SESSION_ID
413                       'Failed-AVP' = [],
414                       'AVP' = []}
415        = call();
416
417send_ok(Config) ->
418    send_ok(?group(Config)).
419
420%% send_invalid_hdr_bits/1
421%%
422%% Send a request with an incorrect E-bit that a callback ignores.
423
424%% Callback answers.
425send_invalid_hdr_bits([callback, _]) ->
426    #diameter_base_STA{'Result-Code' = 2001,  %% SUCCESS
427                       'Failed-AVP' = [],
428                       'AVP' = []}
429        = call();
430
431%% diameter answers.
432send_invalid_hdr_bits([_,_]) ->
433    #'diameter_base_answer-message'{'Result-Code' = 3008, %% INVALID_HDR_BITS
434                                    'Failed-AVP' = [],
435                                    'AVP' = []}
436        = call();
437
438send_invalid_hdr_bits(Config) ->
439    send_invalid_hdr_bits(?group(Config)).
440
441%% send_missing_avp/1
442%%
443%% Send a request with a missing AVP that a callback answers.
444
445%% diameter answers.
446send_missing_avp([answer, rfc6733]) ->
447    #'diameter_base_answer-message'{'Result-Code' = 5005,  %% MISSING_AVP
448                                    'Failed-AVP' = [_],
449                                    'AVP' = []}
450        = call();
451
452%% Callback answers.
453send_missing_avp([_,_]) ->
454    #diameter_base_STA{'Result-Code' = 5005,  %% MISSING_AVP
455                       'Failed-AVP' = [_],
456                       'AVP' = []}
457        = call();
458
459send_missing_avp(Config) ->
460    send_missing_avp(?group(Config)).
461
462%% send_ignore_missing_avp/1
463%%
464%% Send a request with a missing AVP that a callback ignores.
465
466%% diameter answers.
467send_ignore_missing_avp([answer, rfc6733]) ->
468    #'diameter_base_answer-message'{'Result-Code' = 5005,  %% MISSING_AVP
469                                    'Failed-AVP' = [_],
470                                    'AVP' = []}
471        = call();
472
473%% Callback answers, ignores the error
474send_ignore_missing_avp([_,_]) ->
475    #diameter_base_STA{'Result-Code' = 2001,  %% SUCCESS
476                       'Failed-AVP' = [],
477                       'AVP' = []}
478        = call();
479
480send_ignore_missing_avp(Config) ->
481    send_ignore_missing_avp(?group(Config)).
482
483%% send_5xxx_missing_avp/1
484%%
485%% Send a request with a missing AVP that a callback answers
486%% with {answer_message, 5005}.
487
488%% RFC 6733 allows 5xxx in an answer-message.
489send_5xxx_missing_avp([_, rfc6733]) ->
490    #'diameter_base_answer-message'{'Result-Code' = 5005,  %% MISSING_AVP
491                                    'Failed-AVP' = [_],
492                                    'AVP' = []}
493        = call();
494
495%% RFC 3588 doesn't: sending answer fails.
496send_5xxx_missing_avp([_, rfc3588]) ->
497    {error, timeout} = call();
498
499%% Callback answers, ignores the error
500send_5xxx_missing_avp([_,_]) ->
501    #diameter_base_STA{'Result-Code' = 2001,  %% SUCCESS
502                       'Failed-AVP' = [],
503                       'AVP' = []}
504        = call();
505
506send_5xxx_missing_avp(Config) ->
507    send_5xxx_missing_avp(?group(Config)).
508
509%% send_double_error/1
510%%
511%% Send a request with both an invalid E-bit and a missing AVP.
512
513%% Callback answers with STA.
514send_double_error([callback, _]) ->
515    #diameter_base_STA{'Result-Code' = 5005,  %% MISSING_AVP
516                       'Failed-AVP' = [_],
517                       'AVP' = []}
518        = call();
519
520%% diameter answers with answer-message.
521send_double_error([_,_]) ->
522    #'diameter_base_answer-message'{'Result-Code' = 3008, %% INVALID_HDR_BITS
523                                    'Failed-AVP' = [],
524                                    'AVP' = []}
525        = call();
526
527send_double_error(Config) ->
528    send_double_error(?group(Config)).
529
530%% send_3xxx/1
531%%
532%% Send a request that's answered with a 3xxx result code.
533
534%% Callback answers.
535send_3xxx([_,_]) ->
536    #'diameter_base_answer-message'{'Result-Code' = 3999,
537                                    'Failed-AVP' = [],
538                                    'AVP' = []}
539        = call();
540
541send_3xxx(Config) ->
542    send_3xxx(?group(Config)).
543
544%% send_5xxx/1
545%%
546%% Send a request that's answered with a 5xxx result code.
547
548%% Callback answers but fails since 5xxx isn't allowed in an RFC 3588
549%% answer-message.
550send_5xxx([_, rfc3588]) ->
551    {error, timeout} = call();
552
553%% Callback answers.
554send_5xxx([_,_]) ->
555    #'diameter_base_answer-message'{'Result-Code' = 5999,
556                                    'Failed-AVP' = [],
557                                    'AVP' = []}
558        = call();
559
560send_5xxx(Config) ->
561    send_5xxx(?group(Config)).
562
563%% ===========================================================================
564
565call() ->
566    Name = ?testcase(),
567    diameter:call(?CLIENT,
568                  ?DICT,
569                  #diameter_base_STR
570                  {'Termination-Cause' = ?LOGOUT,
571                   'Auth-Application-Id' = ?COMMON,
572                   'Class' = [?L(Name)]},
573                  [{extra, [Name]}]).
574
575%% ===========================================================================
576%% diameter callbacks
577
578%% peer_up/3
579
580peer_up(_SvcName, _Peer, State) ->
581    State.
582
583%% peer_down/3
584
585peer_down(_SvcName, _Peer, State) ->
586    State.
587
588%% pick_peer/5
589
590pick_peer([Peer], _, ?CLIENT, _State, _Name) ->
591    {ok, Peer}.
592
593%% prepare_request/4
594
595prepare_request(Pkt, ?CLIENT, {_Ref, Caps}, Name) ->
596    {send, prepare(Pkt, Caps, Name)}.
597
598prepare(Pkt0, Caps, send_unknown_application) ->
599    Req = sta(Pkt0, Caps),
600    #diameter_packet{bin = <<H:8/binary, 0:32, T/binary>>}
601        = Pkt
602        = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
603
604    Pkt#diameter_packet{bin = <<H/binary, 23:32, T/binary>>};
605
606prepare(Pkt0, Caps, send_unknown_command) ->
607    Req = sta(Pkt0, Caps),
608    #diameter_packet{bin = <<H:5/binary, 275:24, T/binary>>}
609        = Pkt
610        = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
611
612    Pkt#diameter_packet{bin = <<H/binary, 572:24, T/binary>>};
613
614prepare(Pkt, Caps, T)
615  when T == send_ok;
616       T == send_3xxx;
617       T == send_5xxx ->
618    sta(Pkt, Caps);
619
620prepare(Pkt0, Caps, send_invalid_hdr_bits) ->
621    Req = sta(Pkt0, Caps),
622    %% Set the E-bit to force 3008.
623    #diameter_packet{bin = <<H:34, 0:1, T/bitstring>>}
624        = Pkt
625        = diameter_codec:encode(?DICT, Pkt0#diameter_packet{msg = Req}),
626    Pkt#diameter_packet{bin = <<H:34, 1:1, T/bitstring>>};
627
628prepare(Pkt0, Caps, send_double_error) ->
629    dehost(prepare(Pkt0, Caps, send_invalid_hdr_bits));
630
631prepare(Pkt, Caps, T)
632  when T == send_missing_avp;
633       T == send_ignore_missing_avp;
634       T == send_5xxx_missing_avp ->
635    Req = sta(Pkt, Caps),
636    dehost(diameter_codec:encode(?DICT, Pkt#diameter_packet{msg = Req})).
637
638sta(Pkt, Caps) ->
639    #diameter_packet{msg = Req}
640        = Pkt,
641    #diameter_caps{origin_host  = {OH, _},
642                   origin_realm = {OR, DR}}
643        = Caps,
644    Req#diameter_base_STR{'Session-Id' = diameter:session_id(OH),
645                          'Origin-Host' = OH,
646                          'Origin-Realm' = OR,
647                          'Destination-Realm' = DR}.
648
649%% Strip Origin-Host.
650dehost(#diameter_packet{bin = Bin} = Pkt) ->
651    <<V, Len:24, H:16/binary, T0/binary>>
652        = Bin,
653    {SessionId, T1} = split_avp(T0),
654    {OriginHost, T} = split_avp(T1),
655    Delta = size(OriginHost),
656    Pkt#diameter_packet{bin = <<V, (Len - Delta):24, H/binary,
657                                SessionId/binary,
658                                T/binary>>}.
659
660%% handle_answer/5
661
662handle_answer(Pkt, _Req, ?CLIENT, _Peer, _Name) ->
663    Pkt#diameter_packet.msg.
664
665%% handle_error/5
666
667handle_error(Reason, _Req, ?CLIENT, _Peer, _Name) ->
668    {error, Reason}.
669
670split_avp(<<_:5/binary, Len:24, _/binary>> = Bin) ->
671    L = pad(Len),
672    <<Avp:L/binary, T/binary>> = Bin,
673    {Avp, T}.
674
675pad(N)
676  when 0 == N rem 4 ->
677    N;
678pad(N) ->
679    N - (N rem 4) + 4.
680
681%% handle_request/3
682
683handle_request(#diameter_packet{header = #diameter_header{application_id = 0},
684                                msg = Msg},
685               ?SERVER,
686               {_, Caps}) ->
687    request(Msg, Caps).
688
689request(undefined, _) ->  %% unknown command
690    discard;
691
692request(#diameter_base_STR{'Class' = [Name]} = Req, Caps) ->
693    request(?A(Name), Req, Caps).
694
695request(send_ok, Req, Caps) ->
696    {reply, #diameter_packet{msg = answer(Req, Caps),
697                             errors = [5002]}};  %% UNKNOWN_SESSION_ID
698
699request(send_3xxx, _Req, _Caps) ->
700    {answer_message, 3999};
701
702request(send_5xxx, _Req, _Caps) ->
703    {answer_message, 5999};
704
705request(send_invalid_hdr_bits, Req, Caps) ->
706    %% Default errors field but a non-answer-message and only 3xxx
707    %% errors detected means diameter sets neither Result-Code nor
708    %% Failed-AVP.
709    {reply, #diameter_packet{msg = answer(Req, Caps)}};
710
711request(T, Req, Caps)
712  when T == send_double_error;
713       T == send_missing_avp ->
714    {reply, answer(Req, Caps)};
715
716request(send_ignore_missing_avp, Req, Caps) ->
717    {reply, #diameter_packet{msg = answer(Req, Caps),
718                             errors = false}};  %% ignore errors
719
720request(send_5xxx_missing_avp, _Req, _Caps) ->
721    {answer_message, 5005}.  %% MISSING_AVP
722
723answer(Req, Caps) ->
724    #diameter_base_STR{'Session-Id' = SId}
725        = Req,
726    #diameter_caps{origin_host = {OH,_},
727                   origin_realm = {OR,_}}
728        = Caps,
729    #diameter_base_STA{'Session-Id' = SId,
730                       'Origin-Host' = OH,
731                       'Origin-Realm' = OR,
732                       'Result-Code' = 2001}.  %% SUCCESS
733