aboutsummaryrefslogtreecommitdiffstats
path: root/lib/megaco/src/engine/megaco_messenger_misc.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/megaco/src/engine/megaco_messenger_misc.erl')
-rw-r--r--lib/megaco/src/engine/megaco_messenger_misc.erl409
1 files changed, 409 insertions, 0 deletions
diff --git a/lib/megaco/src/engine/megaco_messenger_misc.erl b/lib/megaco/src/engine/megaco_messenger_misc.erl
new file mode 100644
index 0000000000..3c340a8484
--- /dev/null
+++ b/lib/megaco/src/engine/megaco_messenger_misc.erl
@@ -0,0 +1,409 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2003-2009. 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%
+%%
+
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Misc functions used both from the megaco_messenger module
+%% and the megaco_ack_sender module.
+%%
+%%----------------------------------------------------------------------
+
+-module(megaco_messenger_misc).
+
+%% Application internal export
+-export([encode_body/3,
+ encode_trans_request/2,
+ encode_trans_reply/2,
+ encode_actions/3,
+ send_body/3,
+ send_message/3,
+
+ transform_transaction_reply/2
+ ]).
+
+%% Test functions
+-export([compose_message/3, encode_message/2]).
+
+
+-include_lib("megaco/include/megaco.hrl").
+-include("megaco_message_internal.hrl").
+-include_lib("megaco/src/app/megaco_internal.hrl").
+
+-define(MSG_HDR_SZ, 128). % This is just a guess...
+
+-ifdef(MEGACO_TEST_CODE).
+-define(SIM(Other,Where),
+ fun(Afun,Bfun) ->
+ Kfun = {?MODULE,Bfun},
+ case (catch ets:lookup(megaco_test_data, Kfun)) of
+ [{Kfun,Cfun}] ->
+ Cfun(Afun);
+ _ ->
+ Afun
+ end
+ end(Other,Where)).
+-define(TC_AWAIT_SEND_EVENT(SendFunction),
+ case megaco_tc_controller:lookup(send_function) of
+ {value, {Tag, Pid}} when is_pid(Pid) ->
+ Pid ! {Tag, self(), SendFuncion},
+ receive
+ {Tag, Pid} ->
+ ok
+ end;
+ _ ->
+ ok
+ end).
+-else.
+-define(SIM(Other,Where),Other).
+-define(TC_AWAIT_SEND_EVENT(_),ok).
+-endif.
+
+
+%%----------------------------------------------------------------------
+%% Encode the transaction request
+%%----------------------------------------------------------------------
+
+encode_trans_request(CD, TR) when is_record(TR, 'TransactionRequest') ->
+ ?report_debug(CD, "encode trans request", [TR]),
+ Trans = {transactionRequest, TR},
+ encode_transaction(CD, Trans).
+
+encode_trans_reply(#conn_data{segment_send = SegSend,
+ max_pdu_size = Max,
+ protocol_version = V} = CD, Reply)
+ when (SegSend == infinity) or (is_integer(SegSend) and (SegSend > 0)) and
+ is_integer(V) and (V >= 3) and
+ is_integer(Max) and (Max >= ?MSG_HDR_SZ) ->
+ (catch encode_segmented_trans_reply(CD, Reply));
+encode_trans_reply(CD, TR) when is_record(TR, megaco_transaction_reply) ->
+ ?report_debug(CD, "encode trans reply", [TR]),
+ Trans = {transactionReply, transform_transaction_reply(CD, TR)},
+ encode_transaction(CD, Trans);
+encode_trans_reply(CD, TR) when is_tuple(TR) and
+ (element(1, TR) == 'TransactionReply') ->
+ ?report_debug(CD, "encode trans reply", [TR]),
+ Trans = {transactionReply, TR},
+ encode_transaction(CD, Trans).
+
+
+encode_segmented_trans_reply(#conn_data{max_pdu_size = Max} = CD, Rep) ->
+ #megaco_transaction_reply{transactionResult = Res1} = Rep,
+ case Res1 of
+ {actionReplies, AR} when is_list(AR) andalso (length(AR) >= 1) ->
+ case encode_action_replies(CD, AR) of
+ {Size, EncodedARs} when Size =< (Max - ?MSG_HDR_SZ) ->
+ ?report_debug(CD, "action replies encoded size ok",
+ [Size, Max]),
+ %% No need to segment message: within size limit
+ Res2 = {actionReplies, EncodedARs},
+ TR = Rep#megaco_transaction_reply{transactionResult = Res2},
+ TR2 = transform_transaction_reply(CD, TR),
+ Trans = {transactionReply, TR2},
+ encode_transaction(CD, Trans);
+
+ {Size, EncodecARs} ->
+ ?report_debug(CD,
+ "action replies encoded size to large - "
+ "segment",
+ [Size, Max]),
+ %% Over size limit, so go segment the message
+ encode_segments(CD, Rep, EncodecARs)
+ end;
+ _ ->
+ TR = transform_transaction_reply(CD, Rep),
+ Trans = {transactionReply, TR},
+ encode_transaction(CD, Trans)
+ end.
+
+encode_segments(CD, Reply, EncodecARs) ->
+ encode_segments(CD, Reply, EncodecARs, 1, []).
+
+encode_segments(CD, Reply, [EncodedAR], SN, EncodedSegs) ->
+ Bin = encode_segment(CD, Reply, EncodedAR, SN, 'NULL'),
+ {ok, lists:reverse([{SN, Bin}|EncodedSegs])};
+encode_segments(CD, Reply, [EncodedAR|EncodedARs], SN, EncodedSegs) ->
+ Bin = encode_segment(CD, Reply, EncodedAR, SN, asn1_NOVALUE),
+ encode_segments(CD, Reply, EncodedARs, SN + 1, [{SN, Bin}|EncodedSegs]).
+
+encode_segment(CD, Reply, EncodedAR, SN, SC) ->
+ Res = {actionReplies, [EncodedAR]},
+ TR0 = Reply#megaco_transaction_reply{transactionResult = Res,
+ segmentNumber = SN,
+ segmentationComplete = SC},
+ TR = transform_transaction_reply(CD, TR0),
+ Trans = {transactionReply, TR},
+ case encode_transaction(CD, Trans) of
+ {ok, Bin} ->
+ Bin;
+ Error ->
+ throw(Error)
+ end.
+
+
+encode_transaction(#conn_data{protocol_version = V,
+ encoding_mod = EM,
+ encoding_config = EC} = CD, Trans) ->
+ case (catch EM:encode_transaction(EC, V, Trans)) of
+ {ok, Bin} ->
+ ?SIM({ok, Bin}, encode_trans);
+ {'EXIT', {undef, _}} ->
+ {error, not_implemented};
+ {error, not_implemented} = Error1 ->
+ Error1;
+ {error, Reason} ->
+ incNumErrors(CD#conn_data.conn_handle),
+ {error, {EM, encode_transaction, [EC, V, Trans], Reason}};
+ Error2 ->
+ incNumErrors(CD#conn_data.conn_handle),
+ {error, {EM, encode_transaction, [EC, V, Trans], Error2}}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Encode the action request's
+%%----------------------------------------------------------------------
+
+encode_actions(#conn_data{protocol_version = V} = CD, TraceLabel, ARs) ->
+ ?report_debug(CD, TraceLabel, [ARs]),
+
+ %% Encode the actions
+ EM = CD#conn_data.encoding_mod,
+ EC = CD#conn_data.encoding_config,
+ case (catch EM:encode_action_requests(EC, V, ARs)) of
+ {ok, Bin} when is_binary(Bin) ->
+ ?SIM({ok, Bin}, encode_actions);
+ {'EXIT', {undef, _}} ->
+ incNumErrors(CD#conn_data.conn_handle),
+ Reason = not_implemented,
+ {error, {EM, encode_action_requests, [EC, ARs], Reason}};
+ {error, Reason} ->
+ incNumErrors(CD#conn_data.conn_handle),
+ {error, {EM, encode_action_requests, [EC, ARs], Reason}};
+ Error ->
+ incNumErrors(CD#conn_data.conn_handle),
+ {error, {EM, encode_action_requests, [EC, ARs], Error}}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Encode the action reply's
+%%----------------------------------------------------------------------
+
+encode_action_replies(CD, AR) ->
+ encode_action_replies(CD, AR, 0, []).
+
+encode_action_replies(_, [], Size, Acc) ->
+ {Size, lists:reverse(Acc)};
+encode_action_replies(#conn_data{protocol_version = V,
+ encoding_mod = Mod,
+ encoding_config = Conf} = CD,
+ [AR|ARs], Size, Acc) ->
+ case (catch Mod:encode_action_reply(Conf, V, AR)) of
+ {ok, Bin} when is_binary(Bin) ->
+ encode_action_replies(CD, ARs, Size + size(Bin), [Bin|Acc]);
+ {'EXIT', {undef, _}} ->
+ throw({error, not_implemented});
+ {error, not_implemented} = Error1 ->
+ throw(Error1);
+ {error, Reason} ->
+ incNumErrors(CD#conn_data.conn_handle),
+ throw({error, {Mod, encode_action_reply, [Conf, AR], Reason}});
+ Error ->
+ incNumErrors(CD#conn_data.conn_handle),
+ throw({error, {Mod, encode_action_reply, [Conf, AR], Error}})
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Encode the message body
+%%----------------------------------------------------------------------
+
+encode_body(#conn_data{protocol_version = V} = ConnData,
+ TraceLabel, Body) ->
+ %% Create the message envelope
+ MegaMsg = compose_message(ConnData, V, Body),
+
+ ?report_debug(ConnData, TraceLabel, [MegaMsg]),
+
+ %% Encode the message
+ EM = ConnData#conn_data.encoding_mod,
+ EC = ConnData#conn_data.encoding_config,
+ case (catch EM:encode_message(EC, V, MegaMsg)) of
+ {ok, Bin} when is_binary(Bin) ->
+ ?SIM({ok, Bin}, encode_body);
+ {error, Reason} ->
+ incNumErrors(ConnData#conn_data.conn_handle),
+ {error, {EM, [EC, MegaMsg], Reason}};
+ Error ->
+ incNumErrors(ConnData#conn_data.conn_handle),
+ {error, {EM, [EC, MegaMsg], Error}}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Compose and encode a message
+%%----------------------------------------------------------------------
+compose_message(#conn_data{conn_handle = CH,
+ auth_data = MsgAuth}, V, Body) ->
+ LocalMid = CH#megaco_conn_handle.local_mid,
+ Msg = #'Message'{version = V,
+ mId = LocalMid,
+ messageBody = Body},
+ MegaMsg = #'MegacoMessage'{authHeader = MsgAuth, % BUGBUG: Compute?
+ mess = Msg},
+ MegaMsg.
+
+
+encode_message(#conn_data{protocol_version = Version,
+ encoding_mod = EncodingMod,
+ encoding_config = EncodingConfig}, MegaMsg) ->
+ (catch EncodingMod:encode_message(EncodingConfig, Version, MegaMsg)).
+
+
+%%----------------------------------------------------------------------
+%% Send the message body
+%%----------------------------------------------------------------------
+
+send_body(ConnData, TraceLabel, Body) ->
+ case encode_body(ConnData, TraceLabel, Body) of
+ {ok, Bin} ->
+ send_message(ConnData, false, Bin);
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+
+%%----------------------------------------------------------------------
+%% Send the (encoded) message
+%%----------------------------------------------------------------------
+
+send_message(#conn_data{resend_indication = flag} = ConnData,
+ Resend, Bin) ->
+ do_send_message(ConnData, send_message, Bin, [Resend]);
+
+send_message(#conn_data{resend_indication = true} = ConnData,
+ true, Bin) ->
+ do_send_message(ConnData, resend_message, Bin, []);
+
+send_message(ConnData, _Resend, Bin) ->
+ do_send_message(ConnData, send_message, Bin, []).
+
+do_send_message(ConnData, SendFunc, Bin, Extra) ->
+ %% Send the message
+ #conn_data{send_mod = SendMod,
+ send_handle = SendHandle} = ConnData,
+
+ ?TC_AWAIT_SEND_EVENT(SendFunc),
+
+ ?report_trace(ConnData, "send bytes", [{bytes, Bin},
+ {send_func, SendFunc}]),
+
+ Args = [SendHandle, Bin | Extra],
+ case (catch apply(SendMod, SendFunc, Args)) of
+ ok ->
+ ?SIM({ok, Bin}, send_message);
+ {cancel, Reason} ->
+ ?report_trace(ConnData, "<CANCEL> send_message callback",
+ [{bytes, Bin}, {cancel, Reason}]),
+ {error, {send_message_cancelled, Reason}};
+ {error, Reason} ->
+ incNumErrors(ConnData#conn_data.conn_handle),
+ ?report_important(ConnData, "<ERROR> send_message callback",
+ [{bytes, Bin}, {error, Reason}]),
+ error_msg("failed (error) sending message [using ~w] (~p):"
+ "~n~w", [SendFunc, SendHandle, Reason]),
+ {error, {send_message_failed, Reason}};
+ {'EXIT', Reason} = Error ->
+ incNumErrors(ConnData#conn_data.conn_handle),
+ ?report_important(ConnData, "<ERROR> send_message callback",
+ [{bytes, Bin}, {exit, Reason}]),
+ error_msg("failed (exit) sending message [using ~w] (~p):"
+ "~n~w", [SendFunc, SendHandle, Reason]),
+ {error, {send_message_failed, Error}};
+ Reason ->
+ incNumErrors(ConnData#conn_data.conn_handle),
+ ?report_important(ConnData, "<ERROR> send_message callback",
+ [{bytes, Bin}, {error, Reason}]),
+ error_msg("failed sending message [using ~w] on (~p): "
+ "~n~w", [SendFunc, SendHandle, Reason]),
+ {error, {send_message_failed, Reason}}
+ end.
+
+
+%%%-----------------------------------------------------------------
+%%% Misc internal util functions
+%%%-----------------------------------------------------------------
+
+transform_transaction_reply(#conn_data{protocol_version = V}, TR)
+ when is_integer(V) and (V >= 3) ->
+ #megaco_transaction_reply{transactionId = TransId,
+ immAckRequired = IAR,
+ transactionResult = TransRes,
+ segmentNumber = SegNo,
+ segmentationComplete = SegComplete} = TR,
+ {'TransactionReply', TransId, IAR, TransRes, SegNo, SegComplete};
+transform_transaction_reply(_, TR) ->
+ #megaco_transaction_reply{transactionId = TransId,
+ immAckRequired = IAR,
+ transactionResult = TransRes} = TR,
+ {'TransactionReply', TransId, IAR, TransRes}.
+
+
+%%-----------------------------------------------------------------
+%% Func: error_msg/2
+%% Description: Send an error message
+%%-----------------------------------------------------------------
+
+error_msg(F, A) ->
+ ?megaco_error(F, A).
+
+
+%%-----------------------------------------------------------------
+%% Func: incNumErrors/0, incNumErrors/1, incNumTimerRecovery/1
+%% Description: SNMP counter increment functions
+%%-----------------------------------------------------------------
+
+incNumErrors(CH) ->
+ incNum({CH, medGwyGatewayNumErrors}).
+
+incNum(Cnt) ->
+ case (catch ets:update_counter(megaco_stats, Cnt, 1)) of
+ {'EXIT', {badarg, _R}} ->
+ ets:insert(megaco_stats, {Cnt, 1});
+ Old ->
+ Old
+ end.
+
+%% p(F, A) ->
+%% print(now(), F, A).
+
+%% print(Ts, F, A) ->
+%% io:format("*** [~s] ~p ***"
+%% "~n " ++ F ++ "~n",
+%% [format_timestamp(Ts), self() | A]).
+
+%% format_timestamp(Now) ->
+%% {_N1, _N2, N3} = Now,
+%% {Date, Time} = calendar:now_to_datetime(Now),
+%% {YYYY,MM,DD} = Date,
+%% {Hour,Min,Sec} = Time,
+%% FormatDate =
+%% io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w",
+%% [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]),
+%% lists:flatten(FormatDate).