%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2017. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%% http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%
-module(diameter).
%% Configuration.
-export([start_service/2,
stop_service/1,
add_transport/2,
remove_transport/2,
subscribe/1,
unsubscribe/1]).
%% Traffic.
-export([session_id/1,
origin_state_id/0,
call/3,
call/4]).
%% Information.
-export([services/0,
peer_info/1,
peer_find/1,
service_info/2]).
%% Start/stop the application. In a "real" application this should
%% typically be a consequence of a release file rather than by calling
%% start/stop explicitly.
-export([start/0,
stop/0]).
-export_type([eval/0,
evaluable/0, %% deprecated
decode_format/0,
strict_arities/0,
restriction/0,
message_length/0,
remotes/0,
sequence/0,
app_alias/0,
service_name/0,
capability/0,
peer_filter/0,
peer_ref/0,
service_opt/0,
application_opt/0,
app_module/0,
transport_ref/0,
transport_opt/0,
transport_pred/0,
call_opt/0]).
-export_type(['OctetString'/0,
'Integer32'/0,
'Integer64'/0,
'Unsigned32'/0,
'Unsigned64'/0,
'Float32'/0,
'Float64'/0,
'Grouped'/0,
'Address'/0,
'Time'/0,
'UTF8String'/0,
'DiameterIdentity'/0,
'DiameterURI'/0,
'Enumerated'/0,
'IPFilterRule'/0,
'QoSFilterRule'/0]).
-include_lib("diameter/include/diameter.hrl").
-include("diameter_internal.hrl").
%% ---------------------------------------------------------------------------
%% start/0
%% ---------------------------------------------------------------------------
-spec start()
-> ok
| {error, term()}.
start() ->
application:start(?APPLICATION).
%% ---------------------------------------------------------------------------
%% stop/0
%% ---------------------------------------------------------------------------
-spec stop()
-> ok
| {error, term()}.
stop() ->
application:stop(?APPLICATION).
%% ---------------------------------------------------------------------------
%% start_service/2
%% ---------------------------------------------------------------------------
-spec start_service(service_name(), [service_opt()])
-> ok
| {error, term()}.
start_service(SvcName, Opts)
when is_list(Opts) ->
diameter_config:start_service(SvcName, Opts).
%% ---------------------------------------------------------------------------
%% stop_service/1
%% ---------------------------------------------------------------------------
-spec stop_service(service_name())
-> ok
| {error, term()}.
stop_service(SvcName) ->
diameter_config:stop_service(SvcName).
%% ---------------------------------------------------------------------------
%% services/0
%% ---------------------------------------------------------------------------
-spec services()
-> [service_name()].
services() ->
[Name || {Name, _} <- diameter_service:services()].
%% ---------------------------------------------------------------------------
%% service_info/2
%% ---------------------------------------------------------------------------
-spec service_info(service_name(), atom() | [atom()])
-> any().
service_info(SvcName, Option) ->
diameter_service:info(SvcName, Option).
%% ---------------------------------------------------------------------------
%% peer_info/2
%% ---------------------------------------------------------------------------
-spec peer_info(peer_ref())
-> [tuple()].
peer_info(PeerRef) ->
diameter_service:peer_info(PeerRef).
%% ---------------------------------------------------------------------------
%% peer_find/1
%% ---------------------------------------------------------------------------
-spec peer_find(peer_ref() | pid())
-> {peer_ref(), pid()}
| false.
peer_find(Pid) ->
diameter_peer_fsm:find(Pid).
%% ---------------------------------------------------------------------------
%% add_transport/3
%% ---------------------------------------------------------------------------
-spec add_transport(service_name(), {listen|connect, [transport_opt()]})
-> {ok, transport_ref()}
| {error, term()}.
add_transport(SvcName, {T, Opts} = Cfg)
when is_list(Opts), (T == connect orelse T == listen) ->
diameter_config:add_transport(SvcName, Cfg).
%% ---------------------------------------------------------------------------
%% remove_transport/2
%% ---------------------------------------------------------------------------
-spec remove_transport(service_name(), transport_pred())
-> ok | {error, term()}.
remove_transport(SvcName, Pred) ->
diameter_config:remove_transport(SvcName, Pred).
%% ---------------------------------------------------------------------------
%% subscribe/1
%% ---------------------------------------------------------------------------
-spec subscribe(service_name())
-> true.
subscribe(SvcName) ->
diameter_service:subscribe(SvcName).
%% ---------------------------------------------------------------------------
%% unsubscribe/1
%% ---------------------------------------------------------------------------
-spec unsubscribe(service_name())
-> true.
unsubscribe(SvcName) ->
diameter_service:unsubscribe(SvcName).
%% ---------------------------------------------------------------------------
%% session_id/1
%% ---------------------------------------------------------------------------
-spec session_id('DiameterIdentity'())
-> 'OctetString'().
session_id(Ident) ->
diameter_session:session_id(Ident).
%% ---------------------------------------------------------------------------
%% origin_state_id/0
%% ---------------------------------------------------------------------------
-spec origin_state_id()
-> 'Unsigned32'().
origin_state_id() ->
diameter_session:origin_state_id().
%% ---------------------------------------------------------------------------
%% call/3,4
%% ---------------------------------------------------------------------------
-spec call(service_name(), app_alias(), any(), [call_opt()])
-> any().
call(SvcName, App, Message, Options) ->
diameter_traffic:send_request(SvcName, {alias, App}, Message, Options).
call(SvcName, App, Message) ->
call(SvcName, App, Message, []).
%% ===========================================================================
%% Diameter basic types
-type 'OctetString'() :: iolist().
-type 'Integer32'() :: -2147483647..2147483647.
-type 'Integer64'() :: -9223372036854775807..9223372036854775807.
-type 'Unsigned32'() :: 0..4294967295.
-type 'Unsigned64'() :: 0..18446744073709551615.
-type 'Float32'() :: '-infinity' | float() | infinity.
-type 'Float64'() :: '-infinity' | float() | infinity.
-type 'Grouped'() :: list() | tuple().
%% Diameter derived types
-type 'Address'()
:: inet:ip_address()
| string().
-type 'Time'() :: {{integer(), 1..12, 1..31},
{0..23, 0..59, 0..59}}.
-type 'UTF8String'() :: iolist().
-type 'DiameterIdentity'() :: 'OctetString'().
-type 'DiameterURI'() :: 'OctetString'().
-type 'Enumerated'() :: 'Integer32'().
-type 'IPFilterRule'() :: 'OctetString'().
-type 'QoSFilterRule'() :: 'OctetString'().
%% The handle to a service.
-type service_name()
:: any().
%% Capabilities options/avps on start_service/2 and/or add_transport/2
-type capability()
:: {'Origin-Host', 'DiameterIdentity'()}
| {'Origin-Realm', 'DiameterIdentity'()}
| {'Host-IP-Address', ['Address'()]}
| {'Vendor-Id', 'Unsigned32'()}
| {'Product-Name', 'UTF8String'()}
| {'Supported-Vendor-Id', ['Unsigned32'()]}
| {'Auth-Application-Id', ['Unsigned32'()]}
| {'Vendor-Specific-Application-Id', ['Grouped'()]}
| {'Firmware-Revision', 'Unsigned32'()}.
%% Filters for call/4
-type peer_filter()
:: none
| host
| realm
| {host, any|'DiameterIdentity'()}
| {realm, any|'DiameterIdentity'()}
| {eval, eval()}
| {neg, peer_filter()}
| {all, [peer_filter()]}
| {any, [peer_filter()]}.
-opaque peer_ref()
:: pid().
-type eval()
:: {module(), atom(), list()}
| fun()
| maybe_improper_list(eval(), list()).
-type evaluable()
:: eval().
-type sequence()
:: {'Unsigned32'(), 0..32}.
-type restriction()
:: false
| node
| nodes
| [node()]
| eval().
-type remotes()
:: boolean()
| [node()]
| eval().
-type message_length()
:: 0..16#FFFFFF.
-type decode_format()
:: record
| list
| map
| none
| record_from_map.
-type strict_arities()
:: false
| encode
| decode.
%% Options common to both start_service/2 and add_transport/2.
-type common_opt()
:: {pool_size, pos_integer()}
| {capabilities_cb, eval()}
| {capx_timeout, 'Unsigned32'()}
| {strict_capx, boolean()}
| {strict_mbit, boolean()}
| {disconnect_cb, eval()}
| {dpr_timeout, 'Unsigned32'()}
| {dpa_timeout, 'Unsigned32'()}
| {incoming_maxlen, message_length()}
| {length_errors, exit | handle | discard}
| {connect_timer, 'Unsigned32'()}
| {watchdog_timer, 'Unsigned32'() | {module(), atom(), list()}}
| {watchdog_config, [{okay|suspect, non_neg_integer()}]}
| {spawn_opt, list()}.
%% Options passed to start_service/2
-type service_opt()
:: capability()
| {application, [application_opt()]}
| {restrict_connections, restriction()}
| {sequence, sequence() | eval()}
| {share_peers, remotes()}
| {decode_format, decode_format()}
| {traffic_counters, boolean()}
| {string_decode, boolean()}
| {strict_arities, true | strict_arities()}
| {use_shared_peers, remotes()}
| common_opt().
-type application_opt()
:: {alias, app_alias()}
| {dictionary, module()}
| {module, app_module()}
| {state, any()}
| {call_mutates_state, boolean()}
| {answer_errors, callback|report|discard}
| {request_errors, answer_3xxx|answer|callback}.
-type app_alias()
:: any().
-type app_module()
:: module()
| maybe_improper_list(module(), list())
| #diameter_callback{}.
%% Identifier returned by add_transport/2
-type transport_ref()
:: reference().
%% Options passed to add_transport/2
-type transport_opt()
:: {transport_module, atom()}
| {transport_config, any()}
| {transport_config, any(), 'Unsigned32'() | infinity}
| {applications, [app_alias()]}
| {capabilities, [capability()]}
| common_opt()
| {private, any()}.
%% Predicate passed to remove_transport/2
-type transport_pred()
:: fun((transport_ref(), connect|listen, list()) -> boolean())
| fun((transport_ref(), list()) -> boolean())
| fun((list()) -> boolean())
| transport_ref()
| boolean()
| list()
| {connect|listen, transport_pred()}
| {atom(), atom(), list()}.
%% Options passed to call/4
-type call_opt()
:: {extra, list()}
| {filter, peer_filter()}
| {timeout, 'Unsigned32'()}
| {peer, peer_ref()}
| detach.