PageRenderTime 78ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/megaco/src/engine/megaco_messenger.erl

https://github.com/bsmr-erlang/otp
Erlang | 5371 lines | 3901 code | 623 blank | 847 comment | 25 complexity | a23706a7dfa9de93cd45527b8c44eb56 MD5 | raw file
Possible License(s): BSD-3-Clause, LGPL-2.1, MPL-2.0-no-copyleft-exception, Apache-2.0

Large files files are truncated, but you can click here to view the full file

  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 1999-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. %% Purpose: Send and process a (sequence of) Megaco/H.248 transactions
  23. %%----------------------------------------------------------------------
  24. -module(megaco_messenger).
  25. %% Application internal export
  26. -export([
  27. process_received_message/4, process_received_message/5,
  28. receive_message/4, receive_message/5,
  29. connect/4, connect/5,
  30. disconnect/2,
  31. encode_actions/3,
  32. call/3,
  33. cast/3,
  34. cancel/2,
  35. request_timeout/2,
  36. request_keep_alive_timeout/2,
  37. pending_timeout/3,
  38. reply_timeout/3,
  39. segment_timeout/3,
  40. %% segment_reply_timeout/4,
  41. test_request/5,
  42. test_reply/5
  43. ]).
  44. %% MIB stat functions
  45. -export([
  46. get_stats/0, get_stats/1, get_stats/2,
  47. reset_stats/0, reset_stats/1
  48. ]).
  49. %% Misc functions
  50. -export([
  51. cleanup/2,
  52. which_requests/1, which_replies/1
  53. ]).
  54. %% Module internal export
  55. -export([
  56. process_received_message/6,
  57. handle_request/2,
  58. handle_long_request/2,
  59. connect_remote/3,
  60. disconnect_local/2,
  61. disconnect_remote/3,
  62. send_request_remote/4,
  63. receive_reply_remote/2, receive_reply_remote/3
  64. ]).
  65. -include_lib("megaco/include/megaco.hrl").
  66. -include("megaco_message_internal.hrl").
  67. -include_lib("megaco/src/app/megaco_internal.hrl").
  68. %% N.B. Update cancel/1 with '_' when a new field is added
  69. -record(request,
  70. {trans_id,
  71. remote_mid,
  72. timer_ref, % {short, Ref} | {long, Ref}
  73. init_timer,
  74. init_long_timer,
  75. curr_timer,
  76. version,
  77. bytes, % {send, Data} | {no_send, Data}, Data = binary() | tuple()
  78. send_handle,
  79. user_mod,
  80. user_args,
  81. reply_action, % call | cast
  82. reply_data,
  83. seg_recv = [], % [integer()] (received segments)
  84. init_seg_timer,
  85. seg_timer_ref,
  86. keep_alive_timer, % plain | integer() >= 0
  87. keep_alive_ref % undefined | ref()
  88. }).
  89. %% N.B. Update cancel/1 with '_' when a new field is added
  90. -record(reply,
  91. {
  92. trans_id,
  93. local_mid,
  94. state = prepare, % prepare | eval_request | waiting_for_ack | aborted
  95. pending_timer_ref,
  96. handler = undefined, % pid of the proc executing the callback func
  97. timer_ref,
  98. version,
  99. %% bytes: Sent reply data: not acknowledged
  100. bytes, % binary() | [{integer(), binary(), timer_ref()}]
  101. ack_action, % discard_ack | {handle_ack, Data}
  102. send_handle,
  103. %% segments: Not sent reply data (segments)
  104. segments = [], % [{integer(), binary()}]
  105. user_mod,
  106. user_args
  107. }).
  108. -record(trans_id,
  109. {
  110. mid,
  111. serial
  112. }).
  113. -ifdef(MEGACO_TEST_CODE).
  114. -define(SIM(Other,Where),
  115. fun(Afun,Bfun) ->
  116. Kfun = {?MODULE,Bfun},
  117. case (catch ets:lookup(megaco_test_data, Kfun)) of
  118. [{Kfun,Cfun}] ->
  119. Cfun(Afun);
  120. _ ->
  121. Afun
  122. end
  123. end(Other,Where)).
  124. -define(TC_AWAIT_CANCEL_EVENT(),
  125. case megaco_tc_controller:lookup(block_on_cancel) of
  126. {value, {Tag, Pid}} when is_pid(Pid) ->
  127. Pid ! {Tag, self()},
  128. receive
  129. {Tag, Pid} ->
  130. ok
  131. end;
  132. {value, {sleep, To}} when is_integer(To) andalso (To > 0) ->
  133. receive after To -> ok end;
  134. _ ->
  135. ok
  136. end).
  137. -define(TC_AWAIT_REPLY_EVENT(Info),
  138. case megaco_tc_controller:lookup(block_on_reply) of
  139. {value, {Tag, Pid}} when is_pid(Pid) ->
  140. Pid ! {Tag, self(), Info},
  141. receive
  142. {Tag, Pid} ->
  143. ok
  144. end;
  145. _Whatever ->
  146. %% io:format("Whatever: ~p~n", [Whatever]),
  147. ok
  148. end).
  149. -else.
  150. -define(SIM(Other,Where),Other).
  151. -define(TC_AWAIT_CANCEL_EVENT(),ok).
  152. -define(TC_AWAIT_REPLY_EVENT(_),ok).
  153. -endif.
  154. -define(report_pending_limit_exceeded(ConnData),
  155. ?report_important(ConnData, "<ERROR> pending limit exceeded", [])).
  156. -ifdef(megaco_extended_trace).
  157. -define(rt1(T,F,A),?report_trace(T,F,A)).
  158. -define(rt2(F,A), ?rt1(ignore,F,A)).
  159. -define(rt3(F), ?rt2(F,[])).
  160. -else.
  161. -define(rt1(T,F,A),ok).
  162. -define(rt2(F,A), ok).
  163. -define(rt3(F), ok).
  164. -endif.
  165. %%----------------------------------------------------------------------
  166. %% SNMP statistics handling functions
  167. %%----------------------------------------------------------------------
  168. %%-----------------------------------------------------------------
  169. %% Func: get_stats/0, get_stats/1, get_stats/2
  170. %% Description: Retreive statistics (counters) for TCP
  171. %%-----------------------------------------------------------------
  172. get_stats() ->
  173. megaco_stats:get_stats(megaco_stats).
  174. get_stats(ConnHandleOrCounter) ->
  175. megaco_stats:get_stats(megaco_stats, ConnHandleOrCounter).
  176. get_stats(ConnHandle, Counter) ->
  177. megaco_stats:get_stats(megaco_stats, ConnHandle, Counter).
  178. %%-----------------------------------------------------------------
  179. %% Func: reset_stats/0, reaet_stats/1
  180. %% Description: Reset statistics (counters)
  181. %%-----------------------------------------------------------------
  182. reset_stats() ->
  183. megaco_stats:reset_stats(megaco_stats).
  184. reset_stats(ConnHandleOrCounter) ->
  185. megaco_stats:reset_stats(megaco_stats, ConnHandleOrCounter).
  186. %%----------------------------------------------------------------------
  187. %% cleanup utility functions
  188. %%----------------------------------------------------------------------
  189. cleanup(#megaco_conn_handle{local_mid = LocalMid}, Force)
  190. when (Force =:= true) orelse (Force =:= false) ->
  191. Pat = #reply{trans_id = '$1',
  192. local_mid = LocalMid,
  193. state = '$2',
  194. _ = '_'},
  195. do_cleanup(Pat, Force);
  196. cleanup(LocalMid, Force)
  197. when (Force =:= true) orelse (Force =:= false) ->
  198. Pat = #reply{trans_id = '$1',
  199. local_mid = LocalMid,
  200. state = '$2',
  201. _ = '_'},
  202. do_cleanup(Pat, Force).
  203. do_cleanup(Pat, Force) ->
  204. Match = megaco_monitor:which_replies(Pat),
  205. Reps = [{V1, V2} || [V1, V2] <- Match],
  206. do_cleanup2(Reps, Force).
  207. do_cleanup2([], _) ->
  208. ok;
  209. do_cleanup2([{TransId, aborted}|T], Force = false) ->
  210. megaco_monitor:delete_reply(TransId),
  211. do_cleanup2(T, Force);
  212. do_cleanup2([_|T], Force = false) ->
  213. do_cleanup2(T, Force);
  214. do_cleanup2([{TransId, _State}|T], Force = true) ->
  215. megaco_monitor:delete_reply(TransId),
  216. do_cleanup2(T, Force).
  217. %%----------------------------------------------------------------------
  218. %% which_requests and which_replies utility functions
  219. %%----------------------------------------------------------------------
  220. which_requests(#megaco_conn_handle{local_mid = LocalMid,
  221. remote_mid = RemoteMid}) ->
  222. Pat1 = #trans_id{mid = LocalMid,
  223. serial = '$1', _ = '_'},
  224. Pat2 = #request{trans_id = Pat1,
  225. remote_mid = RemoteMid,
  226. _ = '_'},
  227. Match = megaco_monitor:which_requests(Pat2),
  228. [S || [S] <- Match];
  229. which_requests(LocalMid) ->
  230. Pat1 = #trans_id{mid = LocalMid,
  231. serial = '$1', _ = '_'},
  232. Pat2 = #request{trans_id = Pat1,
  233. remote_mid = '$2', _ = '_'},
  234. Match0 = megaco_monitor:which_requests(Pat2),
  235. Match1 = [{mk_ch(LocalMid, V2), V1} || [V1, V2] <- Match0],
  236. which_requests1(lists:sort(Match1)).
  237. which_requests1([]) ->
  238. [];
  239. which_requests1([{CH, S}|T]) ->
  240. which_requests2(T, CH, [S], []).
  241. which_requests2([], CH, Serials, Reqs) ->
  242. lists:reverse([{CH, Serials}|Reqs]);
  243. which_requests2([{CH, S}|T], CH, Serials, Reqs) ->
  244. which_requests2(T, CH, [S|Serials], Reqs);
  245. which_requests2([{CH1, S}|T], CH2, Serials, Reqs) ->
  246. which_requests2(T, CH1, [S], [{CH2, lists:reverse(Serials)}| Reqs]).
  247. which_replies(#megaco_conn_handle{local_mid = LocalMid,
  248. remote_mid = RemoteMid}) ->
  249. Pat1 = #trans_id{mid = RemoteMid,
  250. serial = '$1', _ = '_'},
  251. Pat2 = #reply{trans_id = Pat1,
  252. local_mid = LocalMid,
  253. state = '$2',
  254. handler = '$3', _ = '_'},
  255. Match = megaco_monitor:which_replies(Pat2),
  256. [{V1, V2, V3} || [V1, V2, V3] <- Match];
  257. which_replies(LocalMid) ->
  258. Pat1 = #trans_id{mid = '$1',
  259. serial = '$2', _ = '_'},
  260. Pat2 = #reply{trans_id = Pat1,
  261. local_mid = LocalMid,
  262. state = '$3',
  263. handler = '$4', _ = '_'},
  264. Match0 = megaco_monitor:which_replies(Pat2),
  265. Match1 = [{mk_ch(LocalMid,V1),{V2,V3,V4}} || [V1, V2, V3, V4] <- Match0],
  266. which_replies1(lists:sort(Match1)).
  267. which_replies1([]) ->
  268. [];
  269. which_replies1([{CH, Data}|T]) ->
  270. which_replies2(T, CH, [Data], []).
  271. which_replies2([], CH, Data, Reps) ->
  272. lists:reverse([{CH, Data}|Reps]);
  273. which_replies2([{CH, Data}|T], CH, Datas, Reps) ->
  274. which_replies2(T, CH, [Data|Datas], Reps);
  275. which_replies2([{CH1, Data}|T], CH2, Datas, Reps) ->
  276. which_replies2(T, CH1, [Data], [{CH2, lists:reverse(Datas)}| Reps]).
  277. mk_ch(LM, RM) ->
  278. #megaco_conn_handle{local_mid = LM, remote_mid = RM}.
  279. %%----------------------------------------------------------------------
  280. %% Register/unreister connections
  281. %%----------------------------------------------------------------------
  282. %% Returns {ok, ConnHandle} | {error, Reason}
  283. autoconnect(RH, RemoteMid, SendHandle, ControlPid, Extra)
  284. when is_record(RH, megaco_receive_handle) ->
  285. ?rt2("autoconnect", [RH, RemoteMid, SendHandle, ControlPid]),
  286. case megaco_config:autoconnect(RH, RemoteMid, SendHandle, ControlPid) of
  287. {ok, ConnData} ->
  288. do_connect(ConnData, Extra);
  289. {error, Reason} ->
  290. {error, Reason}
  291. end;
  292. autoconnect(BadHandle, _CH, _SendHandle, _ControlPid, _Extra) ->
  293. {error, {bad_receive_handle, BadHandle}}.
  294. connect(RH, RemoteMid, SendHandle, ControlPid) ->
  295. Extra = ?default_user_callback_extra,
  296. connect(RH, RemoteMid, SendHandle, ControlPid, Extra).
  297. connect(RH, RemoteMid, SendHandle, ControlPid, Extra)
  298. when is_record(RH, megaco_receive_handle) ->
  299. ?rt2("connect", [RH, RemoteMid, SendHandle, ControlPid, Extra]),
  300. %% The purpose of this is to have a temoporary process, to
  301. %% which one can set up a monitor or link and get a
  302. %% notification when process exits. The entire connect is
  303. %% done in the temporary worker process.
  304. %% When it exits, the connect is either successfully done
  305. %% or it failed.
  306. ConnectorFun =
  307. fun() ->
  308. ConnectResult =
  309. case megaco_config:connect(RH, RemoteMid,
  310. SendHandle, ControlPid) of
  311. {ok, ConnData} ->
  312. do_connect(ConnData, Extra);
  313. {error, Reason} ->
  314. {error, Reason}
  315. end,
  316. ?rt2("connector: connected", [self(), ConnectResult]),
  317. exit({result, ConnectResult})
  318. end,
  319. Flag = process_flag(trap_exit, true),
  320. Connector = erlang:spawn_link(ConnectorFun),
  321. receive
  322. {'EXIT', Connector, {result, ConnectResult}} ->
  323. ?rt2("connect result: received expected connector exit signal",
  324. [Connector, ConnectResult]),
  325. process_flag(trap_exit, Flag),
  326. ConnectResult;
  327. {'EXIT', Connector, OtherReason} ->
  328. ?rt2("connect exit: received unexpected connector exit signal",
  329. [Connector, OtherReason]),
  330. process_flag(trap_exit, Flag),
  331. {error, OtherReason}
  332. end;
  333. connect(BadHandle, _CH, _SendHandle, _ControlPid, _Extra) ->
  334. {error, {bad_receive_handle, BadHandle}}.
  335. do_connect(CD, Extra) ->
  336. CH = CD#conn_data.conn_handle,
  337. Version = CD#conn_data.protocol_version,
  338. UserMod = CD#conn_data.user_mod,
  339. UserArgs = CD#conn_data.user_args,
  340. Args =
  341. case Extra of
  342. ?default_user_callback_extra ->
  343. [CH, Version | UserArgs];
  344. _ ->
  345. [CH, Version, Extra | UserArgs]
  346. end,
  347. ?report_trace(CD, "callback: connect", [Args]),
  348. Res = (catch apply(UserMod, handle_connect, Args)),
  349. ?report_debug(CD, "return: connect", [{return, Res}]),
  350. case Res of
  351. ok ->
  352. ?SIM(ok, do_connect), % do_encode),
  353. monitor_process(CH, CD#conn_data.control_pid);
  354. error ->
  355. megaco_config:disconnect(CH),
  356. {error, {connection_refused, CD, error}};
  357. {error, ED} when is_record(ED,'ErrorDescriptor') ->
  358. megaco_config:disconnect(CH),
  359. {error, {connection_refused, CD, ED}};
  360. _Error ->
  361. warning_msg("connect callback failed: ~w", [Res]),
  362. megaco_config:disconnect(CH),
  363. {error, {connection_refused, CD, Res}}
  364. end.
  365. finish_connect(#conn_data{control_pid = ControlPid} = CD)
  366. when is_pid(ControlPid) andalso (node(ControlPid) =:= node()) ->
  367. ?rt1(CD, "finish local connect", [ControlPid]),
  368. do_finish_connect(CD);
  369. finish_connect(#conn_data{conn_handle = CH,
  370. control_pid = ControlPid} = CD)
  371. when is_pid(ControlPid) andalso (node(ControlPid) =/= node()) ->
  372. ?rt1(CD, "finish remote connect", [ControlPid]),
  373. RemoteNode = node(ControlPid),
  374. UserMonitorPid = whereis(megaco_monitor),
  375. Args = [CH, ControlPid, UserMonitorPid],
  376. case rpc:call(RemoteNode, ?MODULE, connect_remote, Args) of
  377. {ok, ControlMonitorPid} ->
  378. do_finish_connect(CD#conn_data{control_pid = ControlMonitorPid});
  379. {error, Reason} ->
  380. disconnect(CH, {connect_remote, Reason}),
  381. {error, Reason};
  382. {badrpc, Reason} ->
  383. Reason2 = {'EXIT', Reason},
  384. disconnect(CH, {connect_remote, Reason2}),
  385. {error, Reason2}
  386. end.
  387. do_finish_connect(#conn_data{conn_handle = CH,
  388. send_handle = SendHandle,
  389. control_pid = ControlPid} = CD) ->
  390. M = ?MODULE,
  391. F = disconnect_local,
  392. A = [CH],
  393. MFA = {M, F, A},
  394. case megaco_config:finish_connect(CH, SendHandle, ControlPid, MFA) of
  395. {ok, Ref} ->
  396. {ok, CD#conn_data{monitor_ref = Ref}};
  397. {error, Reason} ->
  398. {error, {config_update, Reason}}
  399. end.
  400. monitor_process(CH, ControlPid)
  401. when is_pid(ControlPid) andalso (node(ControlPid) =:= node()) ->
  402. M = ?MODULE,
  403. F = disconnect_local,
  404. A = [CH],
  405. Ref = megaco_monitor:apply_at_exit(M, F, A, ControlPid),
  406. case megaco_config:update_conn_info(CH, monitor_ref, Ref) of
  407. ok ->
  408. ?SIM({ok, CH}, monitor_process_local);
  409. {error, Reason} ->
  410. disconnect(CH, {config_update, Reason}),
  411. {error, Reason}
  412. end;
  413. monitor_process(CH, ControlPid)
  414. when is_pid(ControlPid) andalso (node(ControlPid) =/= node()) ->
  415. RemoteNode = node(ControlPid),
  416. UserMonitorPid = whereis(megaco_monitor),
  417. Args = [CH, ControlPid, UserMonitorPid],
  418. case rpc:call(RemoteNode, ?MODULE, connect_remote, Args) of
  419. {ok, ControlMonitorPid} ->
  420. M = ?MODULE,
  421. F = disconnect_local,
  422. A = [CH],
  423. Ref = megaco_monitor:apply_at_exit(M, F, A, ControlMonitorPid),
  424. case megaco_config:update_conn_info(CH, monitor_ref, Ref) of
  425. ok ->
  426. ?SIM({ok, CH}, monitor_process_remote);
  427. {error, Reason} ->
  428. disconnect(CH, {config_update, Reason}),
  429. {error, Reason}
  430. end;
  431. {error, Reason} ->
  432. disconnect(CH, {connect_remote, Reason}),
  433. {error, Reason};
  434. {badrpc, Reason} ->
  435. Reason2 = {'EXIT', Reason},
  436. disconnect(CH, {connect_remote, Reason2}),
  437. {error, Reason2}
  438. end;
  439. monitor_process(CH, undefined = _ControlPid) ->
  440. %% We have to do this later (setting up the monitor),
  441. %% when the first message arrives. The 'connected' atom is
  442. %% the indication for the first arriving message to finish
  443. %% the connect.
  444. %% This may be the case when an MGC performs a pre-connect
  445. %% in order to speed up the handling of an (expected) connecting
  446. %% MG.
  447. case megaco_config:update_conn_info(CH, monitor_ref, connected) of
  448. ok ->
  449. ?SIM({ok, CH}, monitor_process_local);
  450. {error, Reason} ->
  451. disconnect(CH, {config_update, Reason}),
  452. {error, Reason}
  453. end.
  454. connect_remote(CH, ControlPid, UserMonitorPid)
  455. when node(ControlPid) =:= node() andalso node(UserMonitorPid) =/= node() ->
  456. case megaco_config:lookup_local_conn(CH) of
  457. [_ConnData] ->
  458. UserNode = node(UserMonitorPid),
  459. M = ?MODULE,
  460. F = disconnect_remote,
  461. A = [CH, UserNode],
  462. Ref = megaco_monitor:apply_at_exit(M, F, A, UserMonitorPid),
  463. case megaco_config:connect_remote(CH, UserNode, Ref) of
  464. ok ->
  465. ControlMonitorPid = whereis(megaco_monitor),
  466. ?SIM({ok, ControlMonitorPid}, connect_remote);
  467. {error, Reason} ->
  468. {error, Reason}
  469. end;
  470. [] ->
  471. {error, {no_connection, CH}}
  472. end.
  473. cancel_apply_at_exit({connecting, _ConnectorPid}) ->
  474. ok;
  475. cancel_apply_at_exit(connected) ->
  476. ok;
  477. cancel_apply_at_exit(ControlRef) ->
  478. megaco_monitor:cancel_apply_at_exit(ControlRef).
  479. node_of_control_pid(Pid) when is_pid(Pid) ->
  480. node(Pid);
  481. node_of_control_pid(_) ->
  482. node().
  483. disconnect(ConnHandle, DiscoReason)
  484. when is_record(ConnHandle, megaco_conn_handle) ->
  485. case megaco_config:disconnect(ConnHandle) of
  486. {ok, ConnData, RemoteConnData} ->
  487. ControlRef = ConnData#conn_data.monitor_ref,
  488. cancel_apply_at_exit(ControlRef),
  489. handle_disconnect_callback(ConnData, DiscoReason),
  490. ControlNode = node_of_control_pid(ConnData#conn_data.control_pid),
  491. case ControlNode =:= node() of
  492. true ->
  493. %% Propagate to remote users
  494. CancelFun =
  495. fun(RCD) ->
  496. UserRef = RCD#remote_conn_data.monitor_ref,
  497. cancel_apply_at_exit(UserRef),
  498. RCD#remote_conn_data.user_node
  499. end,
  500. Nodes = lists:map(CancelFun, RemoteConnData),
  501. %% io:format("NODES: ~p~n", [Nodes]),
  502. M = ?MODULE,
  503. F = disconnect,
  504. A = [ConnHandle, DiscoReason],
  505. case rpc:multicall(Nodes, M, F, A) of
  506. {Res, []} ->
  507. Check = fun(ok) -> false;
  508. ({error, {no_connection, _CH}}) -> false;
  509. (_) -> true
  510. end,
  511. case lists:filter(Check, Res) of
  512. [] ->
  513. ok;
  514. Bad ->
  515. {error, {remote_disconnect_error, ConnHandle, Bad}}
  516. end;
  517. {_Res, Bad} ->
  518. {error, {remote_disconnect_crash, ConnHandle, Bad}}
  519. end;
  520. false when (RemoteConnData =:= []) ->
  521. %% Propagate to remote control node
  522. M = ?MODULE,
  523. F = disconnect_remote,
  524. A = [DiscoReason, ConnHandle, node()],
  525. case rpc:call(ControlNode, M, F, A) of
  526. {badrpc, Reason} ->
  527. {error, {'EXIT', Reason}};
  528. Other ->
  529. Other
  530. end
  531. end;
  532. {error, Reason} ->
  533. {error, Reason}
  534. end;
  535. disconnect(BadHandle, Reason) ->
  536. {error, {bad_conn_handle, BadHandle, Reason}}.
  537. disconnect_local(Reason, ConnHandle) ->
  538. disconnect(ConnHandle, {no_controlling_process, Reason}).
  539. disconnect_remote(_Reason, ConnHandle, UserNode) ->
  540. case megaco_config:disconnect_remote(ConnHandle, UserNode) of
  541. [RCD] ->
  542. Ref = RCD#remote_conn_data.monitor_ref,
  543. cancel_apply_at_exit(Ref),
  544. ok;
  545. [] ->
  546. {error, {no_connection, ConnHandle}}
  547. end.
  548. %%----------------------------------------------------------------------
  549. %% Handle incoming message
  550. %%----------------------------------------------------------------------
  551. receive_message(ReceiveHandle, ControlPid, SendHandle, Bin) ->
  552. Extra = ?default_user_callback_extra,
  553. receive_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra).
  554. receive_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra) ->
  555. Opts = [link , {min_heap_size, 5000}],
  556. spawn_opt(?MODULE,
  557. process_received_message,
  558. [ReceiveHandle, ControlPid, SendHandle, Bin, self(), Extra], Opts),
  559. ok.
  560. %% This function is called via the spawn_opt function with the link
  561. %% option, therefor the unlink before the exit.
  562. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Receiver,
  563. Extra) ->
  564. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra),
  565. unlink(Receiver),
  566. exit(normal).
  567. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin) ->
  568. Extra = ?default_user_callback_extra,
  569. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra).
  570. process_received_message(ReceiveHandle, ControlPid, SendHandle, Bin, Extra) ->
  571. Flag = process_flag(trap_exit, true),
  572. case prepare_message(ReceiveHandle, SendHandle, Bin, ControlPid, Extra) of
  573. {ok, ConnData, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
  574. ?rt1(ConnData, "message prepared", [MegaMsg]),
  575. Mess = MegaMsg#'MegacoMessage'.mess,
  576. case Mess#'Message'.messageBody of
  577. {transactions, Transactions} ->
  578. {AckList, ReqList} =
  579. prepare_trans(ConnData, Transactions, [], [], Extra),
  580. handle_acks(AckList, Extra),
  581. case ReqList of
  582. [] ->
  583. ?rt3("no transaction requests"),
  584. ignore;
  585. [Req|Reqs] when (ConnData#conn_data.threaded =:= true) ->
  586. ?rt3("handle requests (spawned)"),
  587. lists:foreach(
  588. fun(R) ->
  589. spawn(?MODULE, handle_request, [R, Extra])
  590. end,
  591. Reqs),
  592. handle_request(Req, Extra);
  593. _ ->
  594. ?rt3("handle requests"),
  595. case handle_requests(ReqList, [], Extra) of
  596. [] ->
  597. ignore;
  598. [LongRequest | More] ->
  599. lists:foreach(
  600. fun(LR) ->
  601. spawn(?MODULE, handle_long_request, [LR, Extra])
  602. end,
  603. More),
  604. handle_long_request(LongRequest, Extra)
  605. end
  606. end;
  607. {messageError, Error} ->
  608. handle_message_error(ConnData, Error, Extra)
  609. end;
  610. {silent_fail, ConnData, {_Code, Reason, Error}} ->
  611. ?report_debug(ConnData, Reason, [no_reply, Error]),
  612. ignore;
  613. {verbose_fail, ConnData, {Code, Reason, Error}} ->
  614. ?report_debug(ConnData, Reason, [Error]),
  615. send_message_error(ConnData, Code, Reason)
  616. end,
  617. process_flag(trap_exit, Flag),
  618. ok.
  619. prepare_message(RH, SH, Bin, Pid, Extra)
  620. when is_record(RH, megaco_receive_handle) andalso is_pid(Pid) ->
  621. ?report_trace(RH, "receive bytes", [{bytes, Bin}]),
  622. EncodingMod = RH#megaco_receive_handle.encoding_mod,
  623. EncodingConfig = RH#megaco_receive_handle.encoding_config,
  624. ProtVersion = RH#megaco_receive_handle.protocol_version,
  625. case (catch EncodingMod:decode_message(EncodingConfig, ProtVersion, Bin)) of
  626. {ok, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
  627. ?report_trace(RH, "receive message", [{message, MegaMsg}]),
  628. Mess = MegaMsg#'MegacoMessage'.mess,
  629. RemoteMid = Mess#'Message'.mId,
  630. Version = Mess#'Message'.version,
  631. LocalMid = RH#megaco_receive_handle.local_mid,
  632. CH = #megaco_conn_handle{local_mid = LocalMid,
  633. remote_mid = RemoteMid},
  634. case megaco_config:lookup_local_conn(CH) of
  635. %%
  636. %% Message is not of the negotiated version
  637. %%
  638. [#conn_data{protocol_version = NegVersion,
  639. strict_version = true} = ConnData]
  640. when NegVersion =/= Version ->
  641. %% Use already established connection,
  642. %% but incorrect version
  643. ?rt1(ConnData, "not negotiated version", [Version]),
  644. Error = {error, {not_negotiated_version,
  645. NegVersion, Version}},
  646. handle_syntax_error_callback(RH, ConnData,
  647. prepare_error(Error),
  648. Extra);
  649. [ConnData] ->
  650. %%
  651. %% Use an already established connection
  652. %%
  653. %% This *may* have been set up in the
  654. %% "non-official" way, so we may need to
  655. %% create the monitor to the control process
  656. %% and store the SendHandle (which is normally
  657. %% done when creating the "temporary" connection).
  658. %%
  659. ?rt1(ConnData, "use already established connection", []),
  660. ConnData2 = ConnData#conn_data{send_handle = SH,
  661. control_pid = Pid,
  662. protocol_version = Version},
  663. check_message_auth(CH, ConnData2, MegaMsg, Bin);
  664. [] ->
  665. %% Setup a temporary connection
  666. ?rt3("setup a temporary connection"),
  667. case autoconnect(RH, RemoteMid, SH, Pid, Extra) of
  668. {ok, _} ->
  669. do_prepare_message(RH, CH, SH, MegaMsg, Pid, Bin);
  670. {error, {already_connected, _ConnHandle}} ->
  671. do_prepare_message(RH, CH, SH, MegaMsg, Pid, Bin);
  672. {error, {connection_refused, ConnData, Reason}} ->
  673. Error = prepare_error({error, {connection_refused, Reason}}),
  674. {verbose_fail, ConnData, Error};
  675. {error, Reason} ->
  676. ConnData = fake_conn_data(RH, RemoteMid, SH, Pid),
  677. ConnData2 = ConnData#conn_data{protocol_version = Version},
  678. Error = prepare_error({error, Reason}),
  679. {verbose_fail, ConnData2, Error}
  680. end
  681. end;
  682. Error ->
  683. ?rt2("decode error", [Error]),
  684. ConnData = handle_decode_error(Error,
  685. RH, SH, Bin, Pid,
  686. EncodingMod,
  687. EncodingConfig,
  688. ProtVersion),
  689. handle_syntax_error_callback(RH, ConnData, prepare_error(Error), Extra)
  690. end;
  691. prepare_message(RH, SendHandle, _Bin, ControlPid, _Extra) ->
  692. ConnData = fake_conn_data(RH, SendHandle, ControlPid),
  693. Error = prepare_error({'EXIT', {bad_receive_handle, RH}}),
  694. {verbose_fail, ConnData, Error}.
  695. handle_decode_error({error, {unsupported_version, _}},
  696. #megaco_receive_handle{local_mid = LocalMid} = RH, SH,
  697. Bin, Pid,
  698. EM, EC, V) ->
  699. case (catch EM:decode_mini_message(EC, V, Bin)) of
  700. {ok, #'MegacoMessage'{mess = #'Message'{version = _Ver,
  701. mId = RemoteMid}}} ->
  702. ?rt2("erroneous message received", [SH, RemoteMid, _Ver]),
  703. CH = #megaco_conn_handle{local_mid = LocalMid,
  704. remote_mid = RemoteMid},
  705. incNumErrors(CH),
  706. %% We cannot put the version into conn-data, that will
  707. %% make the resulting error message impossible to sent
  708. %% (unsupported version)
  709. case megaco_config:lookup_local_conn(CH) of
  710. [ConnData] ->
  711. ?rt3("known to us"),
  712. ConnData#conn_data{send_handle = SH};
  713. [] ->
  714. ?rt3("unknown to us"),
  715. ConnData = fake_conn_data(RH, SH, Pid),
  716. ConnData#conn_data{conn_handle = CH}
  717. end;
  718. _ ->
  719. ?rt2("erroneous message received", [SH]),
  720. incNumErrors(),
  721. fake_conn_data(RH, SH, Pid)
  722. end;
  723. handle_decode_error(_,
  724. #megaco_receive_handle{local_mid = LocalMid} = RH, SH,
  725. Bin, Pid,
  726. EM, EC, V) ->
  727. case (catch EM:decode_mini_message(EC, V, Bin)) of
  728. {ok, #'MegacoMessage'{mess = #'Message'{version = Ver,
  729. mId = RemoteMid}}} ->
  730. ?rt2("erroneous message received", [SH, Ver, RemoteMid]),
  731. CH = #megaco_conn_handle{local_mid = LocalMid,
  732. remote_mid = RemoteMid},
  733. incNumErrors(CH),
  734. case megaco_config:lookup_local_conn(CH) of
  735. [ConnData] ->
  736. ?rt3("known to us"),
  737. ConnData#conn_data{send_handle = SH,
  738. protocol_version = Ver};
  739. [] ->
  740. ?rt3("unknown to us"),
  741. ConnData = fake_conn_data(RH, SH, Pid),
  742. ConnData#conn_data{conn_handle = CH,
  743. protocol_version = Ver}
  744. end;
  745. _ ->
  746. ?rt2("erroneous message received", [SH]),
  747. incNumErrors(),
  748. fake_conn_data(RH, SH, Pid)
  749. end.
  750. do_prepare_message(RH, CH, SendHandle, MegaMsg, ControlPid, Bin) ->
  751. case megaco_config:lookup_local_conn(CH) of
  752. [ConnData] ->
  753. case check_message_auth(CH, ConnData, MegaMsg, Bin) of
  754. {ok, ConnData2, MegaMsg} ->
  755. %% Let the connection be permanent
  756. {ok, ConnData2, MegaMsg};
  757. {ReplyTag, ConnData, Reason} ->
  758. %% Remove the temporary connection
  759. disconnect(CH, {bad_auth, Reason}),
  760. {ReplyTag, ConnData, Reason}
  761. end;
  762. [] ->
  763. Reason = no_connection,
  764. disconnect(CH, Reason),
  765. RemoteMid = CH#megaco_conn_handle.remote_mid,
  766. ConnData = fake_conn_data(RH, RemoteMid, SendHandle, ControlPid),
  767. Error = prepare_error({error, Reason}),
  768. {silent_fail, ConnData, Error}
  769. end.
  770. check_message_auth(_ConnHandle, ConnData, MegaMsg, Bin) ->
  771. MsgAuth = MegaMsg#'MegacoMessage'.authHeader,
  772. Mess = MegaMsg#'MegacoMessage'.mess,
  773. Version = Mess#'Message'.version,
  774. ConnData2 = ConnData#conn_data{protocol_version = Version},
  775. ConnAuth = ConnData2#conn_data.auth_data,
  776. ?report_trace(ConnData2, "check message auth", [{bytes, Bin}]),
  777. if
  778. (MsgAuth =:= asn1_NOVALUE) andalso (ConnAuth =:= asn1_NOVALUE) ->
  779. ?SIM({ok, ConnData2, MegaMsg}, check_message_auth);
  780. true ->
  781. ED = #'ErrorDescriptor'{errorCode = ?megaco_unauthorized,
  782. errorText = "Autentication is not supported"},
  783. {verbose_fail, ConnData2, prepare_error({error, ED})}
  784. end.
  785. handle_syntax_error_callback(ReceiveHandle, ConnData, PrepError, Extra) ->
  786. {Code, Reason, Error} = PrepError,
  787. ErrorDesc = #'ErrorDescriptor'{errorCode = Code, errorText = Reason},
  788. Version =
  789. case Error of
  790. {error, {unsupported_version, UV}} ->
  791. UV;
  792. _ ->
  793. ConnData#conn_data.protocol_version
  794. end,
  795. UserMod = ConnData#conn_data.user_mod,
  796. UserArgs = ConnData#conn_data.user_args,
  797. ?report_trace(ReceiveHandle, "callback: syntax error", [ErrorDesc, Error]),
  798. Args =
  799. case Extra of
  800. ?default_user_callback_extra ->
  801. [ReceiveHandle, Version, ErrorDesc | UserArgs];
  802. _ ->
  803. [ReceiveHandle, Version, ErrorDesc, Extra | UserArgs]
  804. end,
  805. Res = (catch apply(UserMod, handle_syntax_error, Args)),
  806. ?report_debug(ReceiveHandle, "return: syntax error",
  807. [{return, Res}, ErrorDesc, Error]),
  808. case Res of
  809. reply ->
  810. {verbose_fail, ConnData, PrepError};
  811. {reply,#'ErrorDescriptor'{errorCode = Code1, errorText = Reason1}} ->
  812. {verbose_fail, ConnData, {Code1,Reason1,Error}};
  813. no_reply ->
  814. {silent_fail, ConnData, PrepError};
  815. {no_reply,#'ErrorDescriptor'{errorCode=Code2,errorText=Reason2}} ->
  816. {silent_fail, ConnData, {Code2,Reason2,Error}}; %%% OTP-????
  817. _ ->
  818. warning_msg("syntax error callback failed: ~w", [Res]),
  819. {verbose_fail, ConnData, PrepError}
  820. end.
  821. fake_conn_data(CH) when is_record(CH, megaco_conn_handle) ->
  822. case (catch megaco_config:conn_info(CH, receive_handle)) of
  823. RH when is_record(RH, megaco_receive_handle) ->
  824. RemoteMid = CH#megaco_conn_handle.remote_mid,
  825. ConnData =
  826. fake_conn_data(RH, RemoteMid, no_send_handle, no_control_pid),
  827. ConnData#conn_data{conn_handle = CH};
  828. {'EXIT', _} ->
  829. UserMid = CH#megaco_conn_handle.local_mid,
  830. case catch megaco_config:user_info(UserMid, receive_handle) of
  831. {'EXIT', _} -> % No such user
  832. #conn_data{conn_handle = CH,
  833. serial = undefined_serial,
  834. control_pid = no_control_pid,
  835. monitor_ref = undefined_monitor_ref,
  836. send_mod = no_send_mod,
  837. send_handle = no_send_handle,
  838. encoding_mod = no_encoding_mod,
  839. encoding_config = no_encoding_config,
  840. reply_action = undefined,
  841. sent_pending_limit = infinity,
  842. recv_pending_limit = infinity};
  843. RH ->
  844. ConnData =
  845. fake_conn_data(RH, no_send_handle, no_control_pid),
  846. ConnData#conn_data{conn_handle = CH}
  847. end
  848. end.
  849. fake_conn_data(RH, SendHandle, ControlPid) ->
  850. fake_conn_data(RH, unknown_remote_mid, SendHandle, ControlPid).
  851. fake_conn_data(RH, RemoteMid, SendHandle, ControlPid) ->
  852. case catch megaco_config:init_conn_data(RH, RemoteMid, SendHandle, ControlPid) of
  853. {'EXIT', _} -> % No such user
  854. fake_user_data(RH, RemoteMid, SendHandle, ControlPid);
  855. ConnData ->
  856. ConnData
  857. end.
  858. fake_user_data(RH, RemoteMid, SendHandle, ControlPid) ->
  859. LocalMid = RH#megaco_receive_handle.local_mid,
  860. RH2 = RH#megaco_receive_handle{local_mid = default},
  861. case catch megaco_config:init_conn_data(RH2, RemoteMid, SendHandle, ControlPid) of
  862. {'EXIT', _} -> % Application stopped?
  863. ConnHandle = #megaco_conn_handle{local_mid = LocalMid,
  864. remote_mid = RemoteMid},
  865. EncodingMod = RH#megaco_receive_handle.encoding_mod,
  866. EncodingConfig = RH#megaco_receive_handle.encoding_config,
  867. SendMod = RH#megaco_receive_handle.send_mod,
  868. #conn_data{conn_handle = ConnHandle,
  869. serial = undefined_serial,
  870. control_pid = ControlPid,
  871. monitor_ref = undefined_monitor_ref,
  872. send_mod = SendMod,
  873. send_handle = SendHandle,
  874. encoding_mod = EncodingMod,
  875. encoding_config = EncodingConfig,
  876. reply_action = undefined,
  877. sent_pending_limit = infinity,
  878. recv_pending_limit = infinity};
  879. ConnData ->
  880. ConnData
  881. end.
  882. prepare_error(Error) ->
  883. case Error of
  884. {error, ED} when is_record(ED, 'ErrorDescriptor') ->
  885. Code = ED#'ErrorDescriptor'.errorCode,
  886. Reason = ED#'ErrorDescriptor'.errorText,
  887. {Code, Reason, Error};
  888. {error, [{reason, {bad_token, [BadToken, _Acc]}, Line}]} when is_integer(Line) ->
  889. Reason =
  890. lists:flatten(
  891. io_lib:format("Illegal token (~p) on line ~w", [BadToken, Line])),
  892. Code = ?megaco_bad_request,
  893. {Code, Reason, Error};
  894. {error, [{reason, {bad_token, _}, Line}]} when is_integer(Line) ->
  895. Reason = lists:concat(["Illegal token on line ", Line]),
  896. Code = ?megaco_bad_request,
  897. {Code, Reason, Error};
  898. {error, [{reason, {Line, _ParserMod, RawReasonString}} | _]} when is_integer(Line) andalso is_list(RawReasonString) ->
  899. Reason =
  900. case RawReasonString of
  901. [[$s, $y, $n, $t, $a, $x | _], TokenString] ->
  902. lists:flatten(
  903. io_lib:format("Syntax error on line ~w before token ~s", [Line, TokenString]));
  904. _ ->
  905. lists:flatten(io_lib:format("Syntax error on line ~w", [Line]))
  906. end,
  907. Code = ?megaco_bad_request,
  908. {Code, Reason, Error};
  909. {error, [{reason, {Line, _, _}} | _]} when is_integer(Line) ->
  910. Reason = lists:concat(["Syntax error on line ", Line]),
  911. Code = ?megaco_bad_request,
  912. {Code, Reason, Error};
  913. {error, {connection_refused, ED}} when is_record(ED,'ErrorDescriptor') ->
  914. Code = ED#'ErrorDescriptor'.errorCode,
  915. Reason = ED#'ErrorDescriptor'.errorText,
  916. {Code, Reason, Error};
  917. {error, {connection_refused, _}} ->
  918. Reason = "Connection refused by user",
  919. Code = ?megaco_unauthorized,
  920. {Code, Reason, Error};
  921. {error, {unsupported_version, V}} ->
  922. Reason =
  923. lists:flatten(io_lib:format("Unsupported version: ~w",[V])),
  924. Code = ?megaco_version_not_supported,
  925. {Code, Reason, Error};
  926. {error, {not_negotiated_version, NegV, MsgV}} ->
  927. Reason =
  928. lists:flatten(
  929. io_lib:format("Not negotiated version: ~w [negotiated ~w]",
  930. [MsgV, NegV])),
  931. Code = ?megaco_version_not_supported,
  932. {Code, Reason, Error};
  933. {error, _} ->
  934. Reason = "Syntax error",
  935. Code = ?megaco_bad_request,
  936. {Code, Reason, Error};
  937. {ok, MegaMsg} when is_record(MegaMsg, 'MegacoMessage') ->
  938. Reason = "MID does not match config",
  939. Code = ?megaco_incorrect_identifier,
  940. {Code, Reason, Error};
  941. _ ->
  942. Reason = "Fatal syntax error",
  943. Code = ?megaco_internal_gateway_error,
  944. {Code, Reason, Error}
  945. end.
  946. prepare_trans(_ConnData, [], AckList, ReqList, _Extra) ->
  947. ?SIM({AckList, ReqList}, prepare_trans_done);
  948. prepare_trans(ConnData, Trans, AckList, ReqList, Extra)
  949. when ConnData#conn_data.monitor_ref =:= undefined_auto_monitor_ref ->
  950. ?rt3("prepare_trans - autoconnect"),
  951. %% <BUGBUG>
  952. %% Do we need something here, if we send more then one
  953. %% trans per message?
  954. %% </BUGBUG>
  955. %% May occur if another process has already setup a
  956. %% temporary connection, but the handle_connect callback
  957. %% function has not yet returned before the eager MG
  958. %% re-sends its initial service change message.
  959. prepare_autoconnecting_trans(ConnData, Trans, AckList, ReqList, Extra);
  960. prepare_trans(#conn_data{monitor_ref = connected} = ConnData,
  961. Trans, AckList, ReqList, Extra) ->
  962. ?rt3("prepare_trans - connected"),
  963. %%
  964. %% This will happen when the "MGC" user performs a "pre" connect,
  965. %% instead of waiting for the auto-connect (which normally
  966. %% happen when the MGC receives the first message from the
  967. %% MG).
  968. %%
  969. %%
  970. %% The monitor_ref will have this value when the pre-connect
  971. %% is complete, so we finish it here and then continue with the
  972. %% normal transaction prepare.
  973. %%
  974. case finish_connect(ConnData) of
  975. {ok, CD} ->
  976. prepare_normal_trans(CD, Trans, AckList, ReqList, Extra);
  977. {error, Reason} ->
  978. disconnect(ConnData#conn_data.conn_handle, Reason),
  979. {[], []}
  980. end;
  981. prepare_trans(#conn_data{monitor_ref = {connecting, _}} = _ConnData,
  982. _Trans, _AckList, _ReqList, _Extra) ->
  983. ?rt3("prepare_trans - connecting"),
  984. %%
  985. %% This will happen when the "MGC" user performs a "pre" connect,
  986. %% instead of waiting for the auto-connect (which normally
  987. %% happen when the MGC receives the first message from the
  988. %% MG).
  989. %%
  990. %%
  991. %% The monitor_ref will have this value when the pre-connect
  992. %% is in progress. We drop (ignore) this message and hope the
  993. %% other side (MG) will resend.
  994. %%
  995. %% prepare_connecting_trans(ConnData, Trans, AckList, ReqList, Extra);
  996. {[], []};
  997. prepare_trans(ConnData, Trans, AckList, ReqList, Extra) ->
  998. ?rt3("prepare_trans - normal"),
  999. %% Handle transaction in the normal case
  1000. prepare_normal_trans(ConnData, Trans, AckList, ReqList, Extra).
  1001. prepare_autoconnecting_trans(_ConnData, [], AckList, ReqList, _Extra) ->
  1002. ?SIM({AckList, ReqList}, prepare_autoconnecting_trans_done);
  1003. prepare_autoconnecting_trans(ConnData, [Trans | Rest], AckList, ReqList, Extra) ->
  1004. ?rt1(ConnData, "[autoconnecting] prepare trans", [Trans]),
  1005. case Trans of
  1006. {transactionRequest, T} when is_record(T, 'TransactionRequest') ->
  1007. Serial = T#'TransactionRequest'.transactionId,
  1008. ConnData2 = ConnData#conn_data{serial = Serial},
  1009. ?report_trace(ConnData2, "Pending handle_connect", [T]),
  1010. %% ------------------------------------------
  1011. %%
  1012. %% Check pending limit
  1013. %%
  1014. %% ------------------------------------------
  1015. Limit = ConnData#conn_data.sent_pending_limit,
  1016. TransId = to_remote_trans_id(ConnData2),
  1017. case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
  1018. ok ->
  1019. send_pending(ConnData2);
  1020. error ->
  1021. %% Pending limit:
  1022. %% In this (granted, highly hypothetical case)
  1023. %% we would make the user very confused if we
  1024. %% called the abort callback function, since
  1025. %% the request callback function has not yet
  1026. %% been called. Alas, we skip this call here.
  1027. send_pending_limit_error(ConnData);
  1028. aborted ->
  1029. ignore
  1030. end,
  1031. prepare_autoconnecting_trans(ConnData2, Rest, AckList, ReqList,
  1032. Extra);
  1033. _ ->
  1034. prepare_autoconnecting_trans(ConnData, Rest, AckList, ReqList,
  1035. Extra)
  1036. end.
  1037. %% =================================================================
  1038. %%
  1039. %% Note that the TransactionReply record was changed i v3 (two
  1040. %% new fields where added), and since we don't know which version,
  1041. %% we cannot use the record definition of TransactionReply.
  1042. %% Instead we transform the record into our own internal format
  1043. %% #megaco_transaction_reply{}
  1044. %%
  1045. %% =================================================================
  1046. prepare_normal_trans(_ConnData, [], AckList, ReqList, _Extra) ->
  1047. ?SIM({AckList, ReqList}, prepare_normal_trans_done);
  1048. prepare_normal_trans(ConnData, [Trans | Rest], AckList, ReqList, Extra) ->
  1049. ?rt1(ConnData, "prepare [normal] trans", [Trans]),
  1050. case Trans of
  1051. {transactionRequest, #'TransactionRequest'{transactionId = asn1_NOVALUE}} ->
  1052. ConnData2 = ConnData#conn_data{serial = 0},
  1053. Code = ?megaco_bad_request,
  1054. Reason = "Syntax error in message: transaction id missing",
  1055. send_trans_error(ConnData2, Code, Reason),
  1056. prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
  1057. {transactionRequest, T} when is_record(T, 'TransactionRequest') ->
  1058. Serial = T#'TransactionRequest'.transactionId,
  1059. ConnData2 = ConnData#conn_data{serial = Serial},
  1060. prepare_request(ConnData2, T, Rest, AckList, ReqList, Extra);
  1061. {transactionPending, T} when is_record(T, 'TransactionPending') ->
  1062. Serial = T#'TransactionPending'.transactionId,
  1063. ConnData2 = ConnData#conn_data{serial = Serial},
  1064. handle_pending(ConnData2, T, Extra),
  1065. prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
  1066. {transactionReply, T} when is_tuple(T) andalso
  1067. (element(1, T) == 'TransactionReply') ->
  1068. T2 = transform_transaction_reply_dec(T),
  1069. Serial = T2#megaco_transaction_reply.transactionId,
  1070. ConnData2 = ConnData#conn_data{serial = Serial},
  1071. handle_reply(ConnData2, T2, Extra),
  1072. prepare_normal_trans(ConnData2, Rest, AckList, ReqList, Extra);
  1073. {transactionResponseAck, List} when is_list(List) ->
  1074. prepare_ack(ConnData, List, Rest, AckList, ReqList, Extra);
  1075. {segmentReply, SR} when is_record(SR, 'SegmentReply') ->
  1076. handle_segment_reply(ConnData, SR, Extra),
  1077. prepare_normal_trans(ConnData, Rest, AckList, ReqList, Extra)
  1078. end.
  1079. prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) ->
  1080. ?rt2("prepare request", [T]),
  1081. ConnHandle = ConnData#conn_data.conn_handle,
  1082. LocalMid = ConnHandle#megaco_conn_handle.local_mid,
  1083. TransId = to_remote_trans_id(ConnData),
  1084. ?rt2("prepare request", [LocalMid, TransId]),
  1085. case lookup_reply(ConnData, TransId) of
  1086. [] ->
  1087. ?rt3("brand new request"),
  1088. %% Brand new request
  1089. %% Check pending limit:
  1090. %%
  1091. %% We should actually check the pending limit here
  1092. %% but since we have to do it later in the
  1093. %% handle_request function (just before we call
  1094. %% the handle_trans_request callback function) we
  1095. %% can just as well wait (this is after all a very
  1096. %% unlikely case: see function prepare_trans when
  1097. %% monitor_ref == undefined_auto_monitor_ref).
  1098. %%
  1099. #conn_data{send_handle = SendHandle,
  1100. pending_timer = InitTimer,
  1101. protocol_version = Version,
  1102. user_mod = UserMod,
  1103. user_args = UserArgs} = ConnData,
  1104. {WaitFor, CurrTimer} = megaco_timer:init(InitTimer),
  1105. M = ?MODULE,
  1106. F = pending_timeout,
  1107. A = [ConnHandle, TransId, CurrTimer],
  1108. PendingRef = megaco_monitor:apply_after(M, F, A, WaitFor),
  1109. Rep = #reply{send_handle = SendHandle,
  1110. trans_id = TransId,
  1111. local_mid = LocalMid,
  1112. pending_timer_ref = PendingRef,
  1113. handler = self(),
  1114. version = Version,
  1115. user_mod = UserMod,
  1116. user_args = UserArgs},
  1117. case megaco_monitor:insert_reply_new(Rep) of
  1118. true ->
  1119. prepare_normal_trans(ConnData, Rest, AckList,
  1120. [{ConnData, TransId, T} | ReqList],
  1121. Extra);
  1122. false ->
  1123. %% Oups - someone got there before we did...
  1124. ?report_debug(ConnData,
  1125. "prepare request: conflicting requests",
  1126. [TransId]),
  1127. send_pending(ConnData),
  1128. megaco_monitor:cancel_apply_after(PendingRef),
  1129. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1130. Extra)
  1131. end;
  1132. %% We can ignore the Converted value here as we *know*
  1133. %% conn-data to be correct (not faked), so even if
  1134. %% the record was converted, it will now have correct
  1135. %% values for user_mod and user_args.
  1136. {_Converted,
  1137. #reply{state = State,
  1138. handler = Pid,
  1139. pending_timer_ref = Ref} = Rep}
  1140. when (State =:= prepare) orelse (State =:= eval_request) ->
  1141. ?rt2("request resend", [State, Pid, Ref]),
  1142. %% Pending limit:
  1143. %% We are still preparing/evaluating the request
  1144. %% Check if the pending limit has been exceeded...
  1145. %% If the pending limit is _not_ exceeded then
  1146. %% we shall send a pending (and actually restart
  1147. %% the pending timer, but that we cannot do).
  1148. %% Don't care about Msg and Rep version diff
  1149. #conn_data{sent_pending_limit = Limit} = ConnData,
  1150. case check_and_maybe_incr_pending_limit(Limit, sent, TransId) of
  1151. ok ->
  1152. %% ------------------------------------------
  1153. %%
  1154. %% Pending limit not exceeded
  1155. %%
  1156. %% 1) Increment number of pendings sent
  1157. %% (done in the check function above)
  1158. %% 2) Send pending message
  1159. %% (We should really restart the pending
  1160. %% timer, but we have no way of doing that).
  1161. %%
  1162. %% ------------------------------------------
  1163. send_pending(ConnData),
  1164. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1165. Extra);
  1166. error ->
  1167. %% -------------------------------------------
  1168. %%
  1169. %% Pending limit exceeded
  1170. %%
  1171. %% 1) Cancel pending timer
  1172. %% 2) Send 506 error message to other side
  1173. %% 3) Inform user (depends on state)
  1174. %% 4) Set reply in aborted state
  1175. %%
  1176. %% -------------------------------------------
  1177. %%
  1178. %% State == eval_request:
  1179. %% This means that the request is currently beeing
  1180. %% evaluated by the user, and the reply timer has
  1181. %% not yet been started.
  1182. %% Either:
  1183. %% a) The "other side" will resend (which will
  1184. %% trigger a pending message send) until we pass the
  1185. %% pending limit
  1186. %% b) We will send pending messages (when the pending
  1187. %% timer expire) until we pass the pending limit.
  1188. %% In any event, we cannot delete the reply record
  1189. %% or the pending counter in this case. Is there
  1190. %% a risk we accumulate aborted reply records?
  1191. %%
  1192. %% State == prepare:
  1193. %% The user does not know about this request
  1194. %% so we can safely perform cleanup.
  1195. %%
  1196. megaco_monitor:cancel_apply_after(Ref),
  1197. send_pending_limit_error(ConnData),
  1198. if
  1199. State == eval_request ->
  1200. %%
  1201. %% What if the user never replies?
  1202. %% In that case we will have a record
  1203. %% (and counters) that is never cleaned up...
  1204. NewFields =
  1205. [{#reply.state, aborted},
  1206. {#reply.pending_timer_ref, undefined}],
  1207. megaco_monitor:update_reply_fields(TransId,
  1208. NewFields),
  1209. handle_request_abort_callback(ConnData,
  1210. TransId, Pid, Extra);
  1211. true ->
  1212. %% Since the user does not know about
  1213. %% this call yet, it is safe to cleanup.
  1214. %% Should we inform?
  1215. Rep2 = Rep#reply{state = aborted},
  1216. cancel_reply(ConnData, Rep2, aborted),
  1217. ok
  1218. end,
  1219. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1220. Extra);
  1221. aborted ->
  1222. %% -------------------------------------------
  1223. %%
  1224. %% Pending limit already exceeded
  1225. %%
  1226. %% Cleanup, just to make sure:
  1227. %% reply record & pending counter
  1228. %%
  1229. %% -------------------------------------------
  1230. Rep2 = Rep#reply{state = aborted},
  1231. cancel_reply(ConnData, Rep2, aborted),
  1232. prepare_normal_trans(ConnData, Rest, AckList, ReqList,
  1233. Extra)
  1234. end;
  1235. %% We can ignore the Converted value here as we *know*
  1236. %% conn-data to be correct (not faked), so even if
  1237. %% the record was converted, it will now have correct
  1238. %% values for user_mod and user_args.
  1239. {_Converted,
  1240. #reply{state = waiting_for_ack,
  1241. bytes = Bin,
  1242. version = Version} = Rep} ->
  1243. ?rt3("request resend when waiting for ack"),
  1244. %% We have already sent a reply, but the receiver
  1245. %% has obviously not got it. Resend the reply but
  1246. %% don't restart the reply_timer.
  1247. ConnData2 = ConnData#conn_data{protocol_version = Version},
  1248. ?report_trace(ConnData2,
  1249. "re-send trans reply", [T | {bytes, Bin}]),

Large files files are truncated, but you can click here to view the full file