aboutsummaryrefslogtreecommitdiffstats
path: root/lib/diameter/examples
diff options
context:
space:
mode:
authorAnders Svensson <[email protected]>2011-05-18 18:29:12 +0200
committerAnders Svensson <[email protected]>2011-05-18 18:29:12 +0200
commit3c15ff32e89e401b4dde2b8acc9699be2614b996 (patch)
tree184dc988fb2ab3af04a532bc59cc794a8d74fbd3 /lib/diameter/examples
parentb1e768e86593178810c8a0b3c38443dcf6be5181 (diff)
downloadotp-3c15ff32e89e401b4dde2b8acc9699be2614b996.tar.gz
otp-3c15ff32e89e401b4dde2b8acc9699be2614b996.tar.bz2
otp-3c15ff32e89e401b4dde2b8acc9699be2614b996.zip
Initial commit of the diameter application.
The application provides an implementation of the Diameter protocol as defined in RFC 3588.
Diffstat (limited to 'lib/diameter/examples')
-rw-r--r--lib/diameter/examples/.gitignore3
-rw-r--r--lib/diameter/examples/GNUmakefile35
-rw-r--r--lib/diameter/examples/client.erl125
-rw-r--r--lib/diameter/examples/client_cb.erl103
-rw-r--r--lib/diameter/examples/peer.erl139
-rw-r--r--lib/diameter/examples/redirect.erl70
-rw-r--r--lib/diameter/examples/redirect_cb.erl63
-rw-r--r--lib/diameter/examples/relay.erl92
-rw-r--r--lib/diameter/examples/relay_cb.erl69
-rw-r--r--lib/diameter/examples/sctp.erl113
-rw-r--r--lib/diameter/examples/server.erl88
-rw-r--r--lib/diameter/examples/server_cb.erl118
12 files changed, 1018 insertions, 0 deletions
diff --git a/lib/diameter/examples/.gitignore b/lib/diameter/examples/.gitignore
new file mode 100644
index 0000000000..d7995d4e6b
--- /dev/null
+++ b/lib/diameter/examples/.gitignore
@@ -0,0 +1,3 @@
+
+*.beam
+
diff --git a/lib/diameter/examples/GNUmakefile b/lib/diameter/examples/GNUmakefile
new file mode 100644
index 0000000000..4c3f87939b
--- /dev/null
+++ b/lib/diameter/examples/GNUmakefile
@@ -0,0 +1,35 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2010-2011. 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
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+EXAMPLES = client server relay # redirect proxy
+
+CALLBACKS = $(EXAMPLES:%=%_cb)
+MODULES = peer $(EXAMPLES) $(EXAMPLES:%=%_cb)
+
+BEAM = $(MODULES:%=%.beam)
+
+%.beam: %.erl
+ erlc -W $<
+
+all: $(BEAM)
+
+clean:
+ rm -f $(BEAM)
+
+.PHONY: all clean
diff --git a/lib/diameter/examples/client.erl b/lib/diameter/examples/client.erl
new file mode 100644
index 0000000000..36a77dd524
--- /dev/null
+++ b/lib/diameter/examples/client.erl
@@ -0,0 +1,125 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% An example Diameter client that can sends base protocol RAR
+%% requests to a connected peer.
+%%
+%% The simplest usage is as follows this to connect to a server
+%% listening on the default port on the local host, assuming diameter
+%% is already started (eg. diameter:start()).
+%%
+%% client:start().
+%% client:connect(tcp).
+%% client:call().
+%%
+%% The first call starts the a service with the default name of
+%% ?MODULE, the second defines a connecting transport that results in
+%% a connection to the peer (if it's listening), the third sends it a
+%% RAR and returns the answer.
+%%
+
+-module(client).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+-export([start/1, %% start a service
+ connect/2, %% add a connecting transport
+ call/1, %% send using the record encoding
+ cast/1, %% send using the list encoding and detached
+ stop/1]). %% stop a service
+%% A real application would typically choose an encoding and whether
+%% they want the call to return the answer or not. Sending with
+%% both the record and list encoding here, one detached and one not,
+%% is just for demonstration purposes.
+
+%% Convenience functions using the default service name, ?SVC_NAME.
+-export([start/0,
+ connect/1,
+ stop/0,
+ call/0,
+ cast/0]).
+
+-define(SVC_NAME, ?MODULE).
+-define(APP_ALIAS, ?MODULE).
+-define(CALLBACK_MOD, client_cb).
+
+-define(L, atom_to_list).
+
+%% The service configuration. As in the server example, a client
+%% supporting multiple Diameter applications may or may not want to
+%% configure a common callback module on all applications.
+-define(SERVICE(Name), [{'Origin-Host', ?L(Name) ++ ".example.com"},
+ {'Origin-Realm', "example.com"},
+ {'Vendor-Id', 0},
+ {'Product-Name', "Client"},
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {application, [{alias, ?APP_ALIAS},
+ {dictionary, ?DIAMETER_DICT_COMMON},
+ {module, ?CALLBACK_MOD}]}]).
+
+%% start/1
+
+start(Name)
+ when is_atom(Name) ->
+ peer:start(Name, ?SERVICE(Name)).
+
+start() ->
+ start(?SVC_NAME).
+
+%% connect/2
+
+connect(Name, T) ->
+ peer:connect(Name, T).
+
+connect(T) ->
+ connect(?SVC_NAME, T).
+
+%% call/1
+
+call(Name) ->
+ SId = diameter:session_id(?L(Name)),
+ RAR = #diameter_base_RAR{'Session-Id' = SId,
+ 'Auth-Application-Id' = 0,
+ 'Re-Auth-Request-Type' = 0},
+ diameter:call(Name, ?APP_ALIAS, RAR, []).
+
+call() ->
+ call(?SVC_NAME).
+
+%% cast/1
+
+cast(Name) ->
+ SId = diameter:session_id(?L(Name)),
+ RAR = ['RAR', {'Session-Id', SId},
+ {'Auth-Application-Id', 0},
+ {'Re-Auth-Request-Type', 1}],
+ diameter:call(Name, ?APP_ALIAS, RAR, [detach]).
+
+cast() ->
+ cast(?SVC_NAME).
+
+%% stop/1
+
+stop(Name) ->
+ peer:stop(Name).
+
+stop() ->
+ stop(?SVC_NAME).
diff --git a/lib/diameter/examples/client_cb.erl b/lib/diameter/examples/client_cb.erl
new file mode 100644
index 0000000000..524a8f94a1
--- /dev/null
+++ b/lib/diameter/examples/client_cb.erl
@@ -0,0 +1,103 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(client_cb).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/4,
+ prepare_request/3,
+ prepare_retransmit/3,
+ handle_answer/4,
+ handle_error/4,
+ handle_request/3]).
+
+%% peer_up/3
+
+peer_up(_SvcName, _Peer, State) ->
+ State.
+
+%% peer_down/3
+
+peer_down(_SvcName, _Peer, State) ->
+ State.
+
+%% pick_peer/4
+
+pick_peer([Peer | _], _, _SvcName, _State) ->
+ {ok, Peer}.
+
+%% prepare_request/3
+
+prepare_request(#diameter_packet{msg = ['RAR' = T | Avps]}, _, {_, Caps}) ->
+ #diameter_caps{origin_host = {OH, DH},
+ origin_realm = {OR, DR}}
+ = Caps,
+
+ {send, [T, {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Destination-Host', DH},
+ {'Destination-Realm', DR}
+ | Avps]};
+
+prepare_request(#diameter_packet{msg = Rec}, _, {_, Caps}) ->
+ #diameter_caps{origin_host = {OH, DH},
+ origin_realm = {OR, DR}}
+ = Caps,
+
+ {send, Rec#diameter_base_RAR{'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Destination-Host' = DH,
+ 'Destination-Realm' = DR}}.
+
+%% prepare_retransmit/3
+
+prepare_retransmit(Packet, SvcName, Peer) ->
+ prepare_request(Packet, SvcName, Peer).
+
+%% handle_answer/4
+
+%% Since client.erl has detached the call when using the list
+%% encoding and not otherwise, output to the terminal in the
+%% the former case, return in the latter.
+
+handle_answer(#diameter_packet{msg = Msg}, Request, _SvcName, _Peer)
+ when is_list(Request) ->
+ io:format("answer: ~p~n", [Msg]);
+
+handle_answer(#diameter_packet{msg = Msg}, _Request, _SvcName, _Peer) ->
+ {ok, Msg}.
+
+%% handle_error/4
+
+handle_error(Reason, Request, _SvcName, _Peer)
+ when is_list(Request) ->
+ io:format("error: ~p~n", [Reason]);
+
+handle_error(Reason, _Request, _SvcName, _Peer) ->
+ {error, Reason}.
+
+%% handle_request/3
+
+handle_request(_Packet, _SvcName, _Peer) ->
+ erlang:error({unexpected, ?MODULE, ?LINE}).
diff --git a/lib/diameter/examples/peer.erl b/lib/diameter/examples/peer.erl
new file mode 100644
index 0000000000..89203e15c3
--- /dev/null
+++ b/lib/diameter/examples/peer.erl
@@ -0,0 +1,139 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% A library module that factors out commonality in the example
+%% Diameter peers.
+%%
+
+-module(peer).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+-export([start/2,
+ listen/2,
+ connect/2,
+ stop/1]).
+
+-type service_name()
+ :: term().
+
+-type protocol()
+ :: tcp | sctp.
+
+-type ip_address()
+ :: default
+ | inet:ip_address().
+
+-type server_config()
+ :: protocol()
+ | {protocol(), ip_address(), non_neg_integer()}.
+
+-type client_config()
+ :: protocol()
+ | {protocol(), ip_address(), non_neg_integer()}
+ | {protocol(), ip_address(), ip_address(), non_neg_integer()}.
+
+-define(DEFAULT_ADDR, {127,0,0,1}).
+-define(DEFAULT_PORT, 3868).
+
+%% ---------------------------------------------------------------------------
+%% Interface functions
+%% ---------------------------------------------------------------------------
+
+%% start/2
+
+-spec start(service_name(), list())
+ -> ok
+ | {error, term()}.
+
+start(Name, Opts)
+ when is_atom(Name), is_list(Opts) ->
+ diameter:start_service(Name, Opts).
+
+%% connect/2
+
+-spec connect(service_name(), client_config())
+ -> {ok, reference()}
+ | {error, term()}.
+
+connect(Name, T) ->
+ diameter:add_transport(Name, {connect, [{reconnect_timer, 5000}
+ | client(T)]}).
+
+%% listen/2
+
+-spec listen(service_name(), server_config())
+ -> {ok, reference()}
+ | {error, term()}.
+
+listen(Name, T) ->
+ diameter:add_transport(Name, {listen, server(T)}).
+
+%% stop/1
+
+-spec stop(service_name())
+ -> ok
+ | {error, term()}.
+
+stop(Name) ->
+ diameter:stop_service(Name).
+
+%% ---------------------------------------------------------------------------
+%% Internal functions
+%% ---------------------------------------------------------------------------
+
+%% server/1
+%%
+%% Return config for a listening transport.
+
+server({T, Addr, Port}) ->
+ [{transport_module, tmod(T)},
+ {transport_config, [{reuseaddr, true},
+ {ip, addr(Addr)},
+ {port, Port}]}];
+
+server(T) ->
+ server({T, ?DEFAULT_ADDR, ?DEFAULT_PORT}).
+
+%% client/1
+%%
+%% Return config for a connecting transport.
+
+client({T, LA, RA, RP}) ->
+ [{transport_module, tmod(T)},
+ {transport_config, [{ip, addr(LA)},
+ {raddr, addr(RA)},
+ {rport, RP},
+ {reuseaddr, true}]}];
+
+client({T, LA, RP}) ->
+ client({T, LA, LA, RP});
+
+client(T) ->
+ client({T, ?DEFAULT_ADDR, ?DEFAULT_ADDR, ?DEFAULT_PORT}).
+
+tmod(tcp) -> diameter_tcp;
+tmod(sctp) -> diameter_sctp.
+
+addr(default) ->
+ ?DEFAULT_ADDR;
+addr(A) ->
+ A.
diff --git a/lib/diameter/examples/redirect.erl b/lib/diameter/examples/redirect.erl
new file mode 100644
index 0000000000..b54701243f
--- /dev/null
+++ b/lib/diameter/examples/redirect.erl
@@ -0,0 +1,70 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(redirect).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+-export([start/1,
+ listen/2,
+ stop/1]).
+
+-export([start/0,
+ listen/1,
+ stop/0]).
+
+-define(APP_ALIAS, ?MODULE).
+-define(SVC_NAME, ?MODULE).
+-define(CALLBACK_MOD, redirect_mod).
+
+%% The service configuration.
+-define(SERVICE(Name), [{'Origin-Host', atom_to_list(Name) ++ ".example.com"},
+ {'Origin-Realm', "example.com"},
+ {'Vendor-Id', 193},
+ {'Product-Name', "RedirectAgent"},
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_RELAY]},
+ {application, [{alias, ?APP_ALIAS},
+ {dictionary, ?DIAMETER_DICT_RELAY},
+ {module, ?CALLBACK_MOD}]}]).
+
+%% start/1
+
+start(Name)
+ when is_atom(Name) ->
+ peer:start(Name, ?SERVICE(Name)).
+
+start() ->
+ start(?SVC_NAME).
+
+%% listen/2
+
+listen(Name, T) ->
+ peer:listen(Name, T).
+
+listen(T) ->
+ listen(?SVC_NAME, T).
+
+%% stop/1
+
+stop(Name) ->
+ peer:stop(Name).
+
+stop() ->
+ stop(?SVC_NAME).
diff --git a/lib/diameter/examples/redirect_cb.erl b/lib/diameter/examples/redirect_cb.erl
new file mode 100644
index 0000000000..ea7ad38749
--- /dev/null
+++ b/lib/diameter/examples/redirect_cb.erl
@@ -0,0 +1,63 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(redirect_cb).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/4,
+ prepare_request/3,
+ prepare_retransmit/3,
+ handle_answer/4,
+ handle_error/4,
+ handle_request/3]).
+
+-define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})).
+
+peer_up(_SvcName, {PeerRef, _}, State) ->
+ io:format("up: ~p~n", [PeerRef]),
+ State.
+
+peer_down(_SvcName, {PeerRef, _}, State) ->
+ io:format("down: ~p~n", [PeerRef]),
+ State.
+
+pick_peer(_, _, _SvcName, _State) ->
+ ?UNEXPECTED.
+
+prepare_request(_, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+prepare_retransmit(_Packet, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+handle_answer(_Packet, _Request, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+handle_error(_Reason, _Request, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+handle_request(#diameter_packet{msg = _, errors = []}, _SvcName, {_, Caps}) ->
+ #diameter_caps{}
+ = Caps,
+ discard. %% TODO
diff --git a/lib/diameter/examples/relay.erl b/lib/diameter/examples/relay.erl
new file mode 100644
index 0000000000..deecb1cfc0
--- /dev/null
+++ b/lib/diameter/examples/relay.erl
@@ -0,0 +1,92 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% An example Diameter relay agent.
+%%
+%% Usage to connect to a server listening on the default port over TCP
+%% and to listen on the default port over SCTP is as follows, assuming
+%% diameter is already started (eg. diameter:start()).
+%%
+%% Eg. relay:start().
+%% relay:connect(tcp).
+%% relay:listen(sctp).
+%%
+
+-module(relay).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+-export([start/1,
+ listen/2,
+ connect/2,
+ stop/1]).
+
+-export([start/0,
+ listen/1,
+ connect/1,
+ stop/0]).
+
+-define(APP_ALIAS, ?MODULE).
+-define(SVC_NAME, ?MODULE).
+-define(CALLBACK_MOD, relay_cb).
+
+%% The service configuration.
+-define(SERVICE(Name), [{'Origin-Host', atom_to_list(Name) ++ ".example.com"},
+ {'Origin-Realm', "example.com"},
+ {'Vendor-Id', 193},
+ {'Product-Name', "RelayAgent"},
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_RELAY]},
+ {application, [{alias, ?MODULE},
+ {dictionary, ?DIAMETER_DICT_RELAY},
+ {module, ?CALLBACK_MOD}]}]).
+
+%% start/1
+
+start(Name)
+ when is_atom(Name) ->
+ peer:start(Name, ?SERVICE(Name)).
+
+start() ->
+ start(?SVC_NAME).
+
+%% listen/2
+
+listen(Name, T) ->
+ peer:listen(Name, T).
+
+listen(T) ->
+ listen(?SVC_NAME, T).
+
+%% connect/2
+
+connect(Name, T) ->
+ peer:connect(Name, T).
+
+connect(T) ->
+ connect(?SVC_NAME, T).
+
+%% stop/1
+
+stop(Name) ->
+ peer:stop(Name).
+
+stop() ->
+ stop(?SVC_NAME).
diff --git a/lib/diameter/examples/relay_cb.erl b/lib/diameter/examples/relay_cb.erl
new file mode 100644
index 0000000000..9ed6517d5c
--- /dev/null
+++ b/lib/diameter/examples/relay_cb.erl
@@ -0,0 +1,69 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(relay_cb).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/5,
+ prepare_request/4,
+ prepare_retransmit/4,
+ handle_answer/5,
+ handle_error/5,
+ handle_request/3]).
+
+peer_up(_SvcName, {PeerRef, _}, State) ->
+ io:format("up: ~p~n", [PeerRef]),
+ State.
+
+peer_down(_SvcName, {PeerRef, _}, State) ->
+ io:format("down: ~p~n", [PeerRef]),
+ State.
+
+%% Returning 'relay' from handle_request causes diameter to resend the
+%% incoming request, which leads to pick_peer and prepare_request
+%% callbacks as if sending explicitly. The 'extra' argument is
+%% appended to the argument list for callbacks following from
+%% resending of the request.
+
+handle_request(_Pkt, _SvcName, _Peer) ->
+ {relay, [{timeout, 1000}, {extra, [relayed]}]}.
+
+%% diameter will filter the sender in the Peers list.
+pick_peer([Peer | _], _, _SvcName, _State, relayed) ->
+ {ok, Peer}.
+
+prepare_request(Pkt, _SvcName, _Peer, relayed) ->
+ {send, Pkt}.
+
+prepare_retransmit(Pkt, _SvcName, _Peer, relayed) ->
+ {send, Pkt}.
+
+%% diameter expects handle_answer to return the diameter_packet record
+%% containing the answer when called for a relayed request.
+
+handle_answer(Pkt, _Request, _SvcName, _Peer, relayed) ->
+ Pkt.
+
+handle_error(Reason, _Request, _SvcName, _Peer, relayed) ->
+ {error, Reason}.
diff --git a/lib/diameter/examples/sctp.erl b/lib/diameter/examples/sctp.erl
new file mode 100644
index 0000000000..2e0e9d8b0b
--- /dev/null
+++ b/lib/diameter/examples/sctp.erl
@@ -0,0 +1,113 @@
+
+-module(sctp).
+
+%%
+%% A small example demonstrating the establishment of an SCTP
+%% association with gen_sctp.
+%%
+
+-export([assoc/0,
+ dbg/0]).
+
+-include_lib("kernel/include/inet_sctp.hrl").
+
+-define(ADDR, {127,0,0,1}).
+-define(SERVER_PORT, 3868).
+-define(CONNECT_TIMEOUT, 2000).
+-define(REQUEST, <<0:64>>).
+-define(REPLY, <<1:64>>).
+
+-record(server, {client,
+ socket,
+ assoc}).
+
+-record(client, {socket,
+ assoc}).
+
+%% assoc/0
+%%
+%% Return on a successfully established association, raise an
+%% exception otherwise.
+
+assoc() ->
+ {_, MRef} = spawn_monitor(fun server/0),
+ receive {'DOWN', MRef, process, _, Info} -> Info end.
+
+%% dbg/0
+
+dbg() ->
+ dbg:tracer(),
+ dbg:p(all,c),
+ dbg:tpl(?MODULE, [{'_',[],[{exception_trace}]}]),
+ dbg:tp(gen_sctp, [{'_',[],[{exception_trace}]}]),
+ ok.
+
+%% server/0
+
+server() ->
+ {ok, Sock} = gen_sctp:open([binary,
+ {reuseaddr, true},
+ {active, true},
+ {ip, ?ADDR},
+ {port, ?SERVER_PORT}]),
+ ok = gen_sctp:listen(Sock, true),
+ {_Pid, MRef} = spawn_monitor(fun client/0),
+ s(#server{client = MRef, socket = Sock}),
+ gen_sctp:close(Sock).
+
+%% s/1
+
+s(#server{} = S) ->
+ s(s(receive T -> T end, S));
+s(T) ->
+ T.
+
+%% s/2
+
+s({sctp, Sock, _RA, _RP, {[], #sctp_assoc_change{state = comm_up,
+ assoc_id = Id}}},
+ #server{socket = Sock, assoc = undefined}
+ = S) ->
+ S#server{assoc = Id};
+
+s({sctp, Sock, _RA, _RP, {[#sctp_sndrcvinfo{assoc_id = AId,
+ stream = SId}],
+ ?REQUEST}},
+ #server{socket = Sock, assoc = AId}
+ = S) ->
+ ok = gen_sctp:send(Sock, AId, SId, ?REPLY),
+ S;
+
+s({'DOWN', MRef, process, _, normal} = T, #server{client = MRef}) ->
+ T.
+
+%% client/0
+
+client() ->
+ {ok, Sock} = gen_sctp:open([binary,
+ {reuseaddr, true},
+ {active, true},
+ {ip, ?ADDR},
+ {port, 0}]),
+ ok = gen_sctp:connect_init(Sock, ?ADDR, ?SERVER_PORT, []),
+ c(#client{socket = Sock}),
+ gen_sctp:close(Sock).
+
+
+%% c/1
+
+c(#client{} = S) ->
+ c(c(receive T -> T end, S));
+c(T) ->
+ T.
+
+c({sctp, Sock, _RA, _RP, {[], #sctp_assoc_change{state = comm_up,
+ assoc_id = Id}}},
+ #client{socket = Sock, assoc = undefined}
+ = S) ->
+ ok = gen_sctp:send(Sock, Id, 0, ?REQUEST),
+ S#client{assoc = Id};
+
+c({sctp, Sock, _RA, _RP, {[#sctp_sndrcvinfo{assoc_id = AId}], ?REPLY}},
+ #client{socket = Sock, assoc = AId}) ->
+ ok.
diff --git a/lib/diameter/examples/server.erl b/lib/diameter/examples/server.erl
new file mode 100644
index 0000000000..ebb408e501
--- /dev/null
+++ b/lib/diameter/examples/server.erl
@@ -0,0 +1,88 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% An example Diameter server that can respond to the base protocol
+%% RAR sent by the client example.
+%%
+%% The simplest example to start a server listening on the loopback
+%% address (which will serve the example usage given in client.erl) is
+%% like this assuming diameter is already started (eg. diameter:start()):
+%%
+%% server:start().
+%% server:listen(tcp).
+%%
+%% The first call starts a service, the second adds a transport listening
+%% on the default port.
+%%
+
+-module(server).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+-export([start/1, %% start a service
+ listen/2, %% add a listening transport
+ stop/1]). %% stop a service
+
+%% Convenience functions using the default service name, ?SVC_NAME.
+-export([start/0,
+ listen/1,
+ stop/0]).
+
+-define(SVC_NAME, ?MODULE).
+-define(APP_ALIAS, ?MODULE).
+-define(CALLBACK_MOD, server_cb).
+
+%% The service configuration. In a server supporting multiple Diameter
+%% applications each application may have its own, although they could all
+%% be configured with a common callback module.
+-define(SERVICE(Name), [{'Origin-Host', atom_to_list(Name) ++ ".example.com"},
+ {'Origin-Realm', "example.com"},
+ {'Vendor-Id', 193},
+ {'Product-Name', "Server"},
+ {'Auth-Application-Id', [?DIAMETER_APP_ID_COMMON]},
+ {application, [{alias, ?APP_ALIAS},
+ {dictionary, ?DIAMETER_DICT_COMMON},
+ {module, ?CALLBACK_MOD}]}]).
+
+%% start/1
+
+start(Name)
+ when is_atom(Name) ->
+ peer:start(Name, ?SERVICE(Name)).
+
+start() ->
+ start(?SVC_NAME).
+
+%% listen/2
+
+listen(Name, T) ->
+ peer:listen(Name, T).
+
+listen(T) ->
+ listen(?SVC_NAME, T).
+
+%% stop/1
+
+stop(Name) ->
+ peer:stop(Name).
+
+stop() ->
+ stop(?SVC_NAME).
diff --git a/lib/diameter/examples/server_cb.erl b/lib/diameter/examples/server_cb.erl
new file mode 100644
index 0000000000..b8705aedfc
--- /dev/null
+++ b/lib/diameter/examples/server_cb.erl
@@ -0,0 +1,118 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2011. 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
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+%%
+%% The diameter application callback module configured by server.erl.
+%%
+
+-module(server_cb).
+
+-include_lib("diameter/include/diameter.hrl").
+-include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl").
+
+%% diameter callbacks
+-export([peer_up/3,
+ peer_down/3,
+ pick_peer/4,
+ prepare_request/3,
+ prepare_retransmit/3,
+ handle_answer/4,
+ handle_error/4,
+ handle_request/3]).
+
+-define(UNEXPECTED, erlang:error({unexpected, ?MODULE, ?LINE})).
+
+peer_up(_SvcName, {PeerRef, _}, State) ->
+ io:format("up: ~p~n", [PeerRef]),
+ State.
+
+peer_down(_SvcName, {PeerRef, _}, State) ->
+ io:format("down: ~p~n", [PeerRef]),
+ State.
+
+pick_peer(_, _, _SvcName, _State) ->
+ ?UNEXPECTED.
+
+prepare_request(_, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+prepare_retransmit(_Packet, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+handle_answer(_Packet, _Request, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+handle_error(_Reason, _Request, _SvcName, _Peer) ->
+ ?UNEXPECTED.
+
+%% A request whose decode was successful ...
+handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps})
+ when is_record(Req, diameter_base_RAR) ->
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}}
+ = Caps,
+ #diameter_base_RAR{'Session-Id' = Id,
+ 'Re-Auth-Request-Type' = RT}
+ = Req,
+
+ {reply, answer(RT, Id, OH, OR)};
+
+%% ... or one that wasn't. 3xxx errors are answered by diameter itself
+%% but these are non-3xxx errors for which we must contruct a reply.
+%% Returning a packet with the non-[] errors field will cause
+%% diameter to add the appropriate result code and Failed-AVPs avps.
+%% We just have to return the relevant answer record with any required
+%% avps.
+handle_request(#diameter_packet{msg = Req} = Pkt, _SvcName, {_, Caps})
+ when is_record(Req, diameter_base_RAR) ->
+ #diameter_caps{origin_host = {OH,_},
+ origin_realm = {OR,_}}
+ = Caps,
+ #diameter_base_RAR{'Session-Id' = Id}
+ = Req,
+
+ Ans = #diameter_base_RAA{'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Session-Id' = Id},
+
+ {reply, Pkt#diameter_packet{msg = Ans}};
+
+%% Should really reply to other base messages that we don't support
+%% but simply discard them instead.
+handle_request(#diameter_packet{}, _SvcName, {_,_}) ->
+ discard.
+
+%% ---------------------------------------------------------------------------
+
+%% Answer using the record or list encoding depending on
+%% Re-Auth-Request-Type. This is just as an example. You would
+%% typically just choose one, and this has nothing to do with the how
+%% client.erl sends.
+
+answer(0, Id, OH, OR) ->
+ #diameter_base_RAA{'Result-Code' = 2001, %% DIAMETER_SUCCESS
+ 'Origin-Host' = OH,
+ 'Origin-Realm' = OR,
+ 'Session-Id' = Id};
+
+answer(_, Id, OH, OR) ->
+ ['RAA', {'Result-Code', 5012}, %% DIAMETER_UNABLE_TO_COMPLY
+ {'Origin-Host', OH},
+ {'Origin-Realm', OR},
+ {'Session-Id', Id}].