1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2003-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%%---------------------------------------------------------------------- 23%% Purpose: Misc functions used both from the megaco_messenger module 24%% and the megaco_ack_sender module. 25%% 26%%---------------------------------------------------------------------- 27 28-module(megaco_messenger_misc). 29 30%% Application internal export 31-export([encode_body/3, 32 encode_trans_request/2, 33 encode_trans_reply/2, 34 encode_actions/3, 35 send_body/3, 36 send_message/3, 37 38 transform_transaction_reply/2 39 ]). 40 41%% Test functions 42-export([compose_message/3, encode_message/2]). 43 44 45-include_lib("megaco/include/megaco.hrl"). 46-include("megaco_message_internal.hrl"). 47-include_lib("megaco/src/app/megaco_internal.hrl"). 48 49-define(MSG_HDR_SZ, 128). % This is just a guess... 50 51-ifdef(MEGACO_TEST_CODE). 52-define(SIM(Other,Where), 53 fun(Afun,Bfun) -> 54 Kfun = {?MODULE,Bfun}, 55 case (catch ets:lookup(megaco_test_data, Kfun)) of 56 [{Kfun,Cfun}] -> 57 Cfun(Afun); 58 _ -> 59 Afun 60 end 61 end(Other,Where)). 62-define(TC_AWAIT_SEND_EVENT(SendFunction), 63 case megaco_tc_controller:lookup(send_function) of 64 {value, {Tag, Pid}} when is_pid(Pid) -> 65 Pid ! {Tag, self(), SendFuncion}, 66 receive 67 {Tag, Pid} -> 68 ok 69 end; 70 _ -> 71 ok 72 end). 73-else. 74-define(SIM(Other,Where),Other). 75-define(TC_AWAIT_SEND_EVENT(_),ok). 76-endif. 77 78 79%%---------------------------------------------------------------------- 80%% Encode the transaction request 81%%---------------------------------------------------------------------- 82 83encode_trans_request(CD, TR) when is_record(TR, 'TransactionRequest') -> 84 ?report_debug(CD, "encode trans request", [TR]), 85 Trans = {transactionRequest, TR}, 86 encode_transaction(CD, Trans). 87 88encode_trans_reply(#conn_data{segment_send = SegSend, 89 max_pdu_size = Max, 90 protocol_version = V} = CD, Reply) 91 when (SegSend == infinity) or (is_integer(SegSend) and (SegSend > 0)) and 92 is_integer(V) and (V >= 3) and 93 is_integer(Max) and (Max >= ?MSG_HDR_SZ) -> 94 (catch encode_segmented_trans_reply(CD, Reply)); 95encode_trans_reply(CD, TR) when is_record(TR, megaco_transaction_reply) -> 96 ?report_debug(CD, "encode trans reply", [TR]), 97 Trans = {transactionReply, transform_transaction_reply(CD, TR)}, 98 encode_transaction(CD, Trans); 99encode_trans_reply(CD, TR) when is_tuple(TR) and 100 (element(1, TR) == 'TransactionReply') -> 101 ?report_debug(CD, "encode trans reply", [TR]), 102 Trans = {transactionReply, TR}, 103 encode_transaction(CD, Trans). 104 105 106encode_segmented_trans_reply(#conn_data{max_pdu_size = Max} = CD, Rep) -> 107 #megaco_transaction_reply{transactionResult = Res1} = Rep, 108 case Res1 of 109 {actionReplies, AR} when is_list(AR) andalso (length(AR) >= 1) -> 110 case encode_action_replies(CD, AR) of 111 {Size, EncodedARs} when Size =< (Max - ?MSG_HDR_SZ) -> 112 ?report_debug(CD, "action replies encoded size ok", 113 [Size, Max]), 114 %% No need to segment message: within size limit 115 Res2 = {actionReplies, EncodedARs}, 116 TR = Rep#megaco_transaction_reply{transactionResult = Res2}, 117 TR2 = transform_transaction_reply(CD, TR), 118 Trans = {transactionReply, TR2}, 119 encode_transaction(CD, Trans); 120 121 {Size, EncodecARs} -> 122 ?report_debug(CD, 123 "action replies encoded size to large - " 124 "segment", 125 [Size, Max]), 126 %% Over size limit, so go segment the message 127 encode_segments(CD, Rep, EncodecARs) 128 end; 129 _ -> 130 TR = transform_transaction_reply(CD, Rep), 131 Trans = {transactionReply, TR}, 132 encode_transaction(CD, Trans) 133 end. 134 135encode_segments(CD, Reply, EncodecARs) -> 136 encode_segments(CD, Reply, EncodecARs, 1, []). 137 138encode_segments(CD, Reply, [EncodedAR], SN, EncodedSegs) -> 139 Bin = encode_segment(CD, Reply, EncodedAR, SN, 'NULL'), 140 {ok, lists:reverse([{SN, Bin}|EncodedSegs])}; 141encode_segments(CD, Reply, [EncodedAR|EncodedARs], SN, EncodedSegs) -> 142 Bin = encode_segment(CD, Reply, EncodedAR, SN, asn1_NOVALUE), 143 encode_segments(CD, Reply, EncodedARs, SN + 1, [{SN, Bin}|EncodedSegs]). 144 145encode_segment(CD, Reply, EncodedAR, SN, SC) -> 146 Res = {actionReplies, [EncodedAR]}, 147 TR0 = Reply#megaco_transaction_reply{transactionResult = Res, 148 segmentNumber = SN, 149 segmentationComplete = SC}, 150 TR = transform_transaction_reply(CD, TR0), 151 Trans = {transactionReply, TR}, 152 case encode_transaction(CD, Trans) of 153 {ok, Bin} -> 154 Bin; 155 Error -> 156 throw(Error) 157 end. 158 159 160encode_transaction(#conn_data{protocol_version = V, 161 encoding_mod = EM, 162 encoding_config = EC} = CD, Trans) -> 163 case (catch EM:encode_transaction(EC, V, Trans)) of 164 {ok, Bin} -> 165 ?SIM({ok, Bin}, encode_trans); 166 {'EXIT', {undef, _}} -> 167 {error, not_implemented}; 168 {error, not_implemented} = Error1 -> 169 Error1; 170 {error, Reason} -> 171 incNumErrors(CD#conn_data.conn_handle), 172 {error, {EM, encode_transaction, [EC, V, Trans], Reason}}; 173 Error2 -> 174 incNumErrors(CD#conn_data.conn_handle), 175 {error, {EM, encode_transaction, [EC, V, Trans], Error2}} 176 end. 177 178 179%%---------------------------------------------------------------------- 180%% Encode the action request's 181%%---------------------------------------------------------------------- 182 183encode_actions(#conn_data{protocol_version = V} = CD, TraceLabel, ARs) -> 184 ?report_debug(CD, TraceLabel, [ARs]), 185 186 %% Encode the actions 187 EM = CD#conn_data.encoding_mod, 188 EC = CD#conn_data.encoding_config, 189 case (catch EM:encode_action_requests(EC, V, ARs)) of 190 {ok, Bin} when is_binary(Bin) -> 191 ?SIM({ok, Bin}, encode_actions); 192 {'EXIT', {undef, _}} -> 193 incNumErrors(CD#conn_data.conn_handle), 194 Reason = not_implemented, 195 {error, {EM, encode_action_requests, [EC, ARs], Reason}}; 196 {error, Reason} -> 197 incNumErrors(CD#conn_data.conn_handle), 198 {error, {EM, encode_action_requests, [EC, ARs], Reason}}; 199 Error -> 200 incNumErrors(CD#conn_data.conn_handle), 201 {error, {EM, encode_action_requests, [EC, ARs], Error}} 202 end. 203 204 205%%---------------------------------------------------------------------- 206%% Encode the action reply's 207%%---------------------------------------------------------------------- 208 209encode_action_replies(CD, AR) -> 210 encode_action_replies(CD, AR, 0, []). 211 212encode_action_replies(_, [], Size, Acc) -> 213 {Size, lists:reverse(Acc)}; 214encode_action_replies(#conn_data{protocol_version = V, 215 encoding_mod = Mod, 216 encoding_config = Conf} = CD, 217 [AR|ARs], Size, Acc) -> 218 case (catch Mod:encode_action_reply(Conf, V, AR)) of 219 {ok, Bin} when is_binary(Bin) -> 220 encode_action_replies(CD, ARs, Size + size(Bin), [Bin|Acc]); 221 {'EXIT', {undef, _}} -> 222 throw({error, not_implemented}); 223 {error, not_implemented} = Error1 -> 224 throw(Error1); 225 {error, Reason} -> 226 incNumErrors(CD#conn_data.conn_handle), 227 throw({error, {Mod, encode_action_reply, [Conf, AR], Reason}}); 228 Error -> 229 incNumErrors(CD#conn_data.conn_handle), 230 throw({error, {Mod, encode_action_reply, [Conf, AR], Error}}) 231 end. 232 233 234%%---------------------------------------------------------------------- 235%% Encode the message body 236%%---------------------------------------------------------------------- 237 238encode_body(#conn_data{protocol_version = V} = ConnData, 239 TraceLabel, Body) -> 240 %% Create the message envelope 241 MegaMsg = compose_message(ConnData, V, Body), 242 243 ?report_debug(ConnData, TraceLabel, [MegaMsg]), 244 245 %% Encode the message 246 EM = ConnData#conn_data.encoding_mod, 247 EC = ConnData#conn_data.encoding_config, 248 case (catch EM:encode_message(EC, V, MegaMsg)) of 249 {ok, Bin} when is_binary(Bin) -> 250 ?SIM({ok, Bin}, encode_body); 251 {error, Reason} -> 252 incNumErrors(ConnData#conn_data.conn_handle), 253 {error, {EM, [EC, MegaMsg], Reason}}; 254 Error -> 255 incNumErrors(ConnData#conn_data.conn_handle), 256 {error, {EM, [EC, MegaMsg], Error}} 257 end. 258 259 260%%---------------------------------------------------------------------- 261%% Compose and encode a message 262%%---------------------------------------------------------------------- 263compose_message(#conn_data{conn_handle = CH, 264 auth_data = MsgAuth}, V, Body) -> 265 LocalMid = CH#megaco_conn_handle.local_mid, 266 Msg = #'Message'{version = V, 267 mId = LocalMid, 268 messageBody = Body}, 269 MegaMsg = #'MegacoMessage'{authHeader = MsgAuth, % BUGBUG: Compute? 270 mess = Msg}, 271 MegaMsg. 272 273 274encode_message(#conn_data{protocol_version = Version, 275 encoding_mod = EncodingMod, 276 encoding_config = EncodingConfig}, MegaMsg) -> 277 (catch EncodingMod:encode_message(EncodingConfig, Version, MegaMsg)). 278 279 280%%---------------------------------------------------------------------- 281%% Send the message body 282%%---------------------------------------------------------------------- 283 284send_body(ConnData, TraceLabel, Body) -> 285 case encode_body(ConnData, TraceLabel, Body) of 286 {ok, Bin} -> 287 send_message(ConnData, false, Bin); 288 {error, Reason} -> 289 {error, Reason} 290 end. 291 292 293%%---------------------------------------------------------------------- 294%% Send the (encoded) message 295%%---------------------------------------------------------------------- 296 297send_message(#conn_data{resend_indication = flag} = ConnData, 298 Resend, Bin) -> 299 do_send_message(ConnData, send_message, Bin, [Resend]); 300 301send_message(#conn_data{resend_indication = true} = ConnData, 302 true, Bin) -> 303 do_send_message(ConnData, resend_message, Bin, []); 304 305send_message(ConnData, _Resend, Bin) -> 306 do_send_message(ConnData, send_message, Bin, []). 307 308do_send_message(ConnData, SendFunc, Bin, Extra) -> 309 %% Send the message 310 #conn_data{send_mod = SendMod, 311 send_handle = SendHandle} = ConnData, 312 313 ?TC_AWAIT_SEND_EVENT(SendFunc), 314 315 ?report_trace(ConnData, "send bytes", [{bytes, Bin}, 316 {send_func, SendFunc}]), 317 318 Args = [SendHandle, Bin | Extra], 319 case (catch apply(SendMod, SendFunc, Args)) of 320 ok -> 321 ?SIM({ok, Bin}, send_message); 322 {cancel, Reason} -> 323 ?report_trace(ConnData, "<CANCEL> send_message callback", 324 [{bytes, Bin}, {cancel, Reason}]), 325 {error, {send_message_cancelled, Reason}}; 326 {error, Reason} -> 327 incNumErrors(ConnData#conn_data.conn_handle), 328 ?report_important(ConnData, "<ERROR> send_message callback", 329 [{bytes, Bin}, {error, Reason}]), 330 error_msg("failed (error) sending message [using ~w] (~p):" 331 "~n~w", [SendFunc, SendHandle, Reason]), 332 {error, {send_message_failed, Reason}}; 333 {'EXIT', Reason} = Error -> 334 incNumErrors(ConnData#conn_data.conn_handle), 335 ?report_important(ConnData, "<ERROR> send_message callback", 336 [{bytes, Bin}, {exit, Reason}]), 337 error_msg("failed (exit) sending message [using ~w] (~p):" 338 "~n~w", [SendFunc, SendHandle, Reason]), 339 {error, {send_message_failed, Error}}; 340 Reason -> 341 incNumErrors(ConnData#conn_data.conn_handle), 342 ?report_important(ConnData, "<ERROR> send_message callback", 343 [{bytes, Bin}, {error, Reason}]), 344 error_msg("failed sending message [using ~w] on (~p): " 345 "~n~w", [SendFunc, SendHandle, Reason]), 346 {error, {send_message_failed, Reason}} 347 end. 348 349 350%%%----------------------------------------------------------------- 351%%% Misc internal util functions 352%%%----------------------------------------------------------------- 353 354transform_transaction_reply(#conn_data{protocol_version = V}, TR) 355 when is_integer(V) and (V >= 3) -> 356 #megaco_transaction_reply{transactionId = TransId, 357 immAckRequired = IAR, 358 transactionResult = TransRes, 359 segmentNumber = SegNo, 360 segmentationComplete = SegComplete} = TR, 361 {'TransactionReply', TransId, IAR, TransRes, SegNo, SegComplete}; 362transform_transaction_reply(_, TR) -> 363 #megaco_transaction_reply{transactionId = TransId, 364 immAckRequired = IAR, 365 transactionResult = TransRes} = TR, 366 {'TransactionReply', TransId, IAR, TransRes}. 367 368 369%%----------------------------------------------------------------- 370%% Func: error_msg/2 371%% Description: Send an error message 372%%----------------------------------------------------------------- 373 374error_msg(F, A) -> 375 ?megaco_error(F, A). 376 377 378%%----------------------------------------------------------------- 379%% Func: incNumErrors/0, incNumErrors/1, incNumTimerRecovery/1 380%% Description: SNMP counter increment functions 381%%----------------------------------------------------------------- 382 383incNumErrors(CH) -> 384 incNum({CH, medGwyGatewayNumErrors}). 385 386incNum(Cnt) -> 387 case (catch ets:update_counter(megaco_stats, Cnt, 1)) of 388 {'EXIT', {badarg, _R}} -> 389 ets:insert(megaco_stats, {Cnt, 1}); 390 Old -> 391 Old 392 end. 393 394%% p(F, A) -> 395%% print(now(), F, A). 396 397%% print(Ts, F, A) -> 398%% io:format("*** [~s] ~p ***" 399%% "~n " ++ F ++ "~n", 400%% [format_timestamp(Ts), self() | A]). 401 402%% format_timestamp(Now) -> 403%% {_N1, _N2, N3} = Now, 404%% {Date, Time} = calendar:now_to_datetime(Now), 405%% {YYYY,MM,DD} = Date, 406%% {Hour,Min,Sec} = Time, 407%% FormatDate = 408%% io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", 409%% [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), 410%% lists:flatten(FormatDate). 411