%%-------------------------------------------------------------------- %% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1999-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% %% %% %%----------------------------------------------------------------- %% File: orber_iiop_pm.erl %% Description: %% This file contains the mapping of addresses on the format {Host, Port} %% to a proxy pid. %% %%----------------------------------------------------------------- -module(orber_iiop_pm). -behaviour(gen_server). -include_lib("orber/src/orber_iiop.hrl"). -include_lib("orber/include/corba.hrl"). -include_lib("kernel/include/inet.hrl"). %%----------------------------------------------------------------- %% External exports %%----------------------------------------------------------------- -export([start/0, start/1]). %%----------------------------------------------------------------- %% Internal exports %%----------------------------------------------------------------- -export([connect/7, close_connection/1, close_connection/2, list_existing_connections/0, list_setup_connections/0, list_all_connections/0, init/1, handle_call/3, handle_cast/2, handle_info/2, code_change/3, terminate/2, stop/0, setup_connection/8, reconfigure/1, reconfigure/3, reconfigure/4, add_connection/3, sockname2peername/2, peername2sockname/2]). %%----------------------------------------------------------------- %% Macros/Defines %%----------------------------------------------------------------- -define(DEBUG_LEVEL, 7). -define(PM_CONNECTION_DB, orber_iiop_pm_db). -record(state, {connections, queue}). -record(connection, {hp, child, interceptors, slave, flags = 0, alias = 0, socketdata = {"Unavailable", 0}}). %%----------------------------------------------------------------- %% External interface functions %%----------------------------------------------------------------- start() -> ignore. start(Opts) -> gen_server:start_link({local, 'orber_iiop_pm'}, ?MODULE, Opts, []). connect(Host, Port, SocketType, Timeout, Chars, Wchars, Ctx) when SocketType == normal -> Key = create_key(Host, Port, Ctx), case ets:lookup(?PM_CONNECTION_DB, Key) of [#connection{child = connecting}] -> gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType, [], Chars, Wchars, Key}, Timeout); [] -> gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType, [], Chars, Wchars, Key}, Timeout); [#connection{hp = {_, _, 0}, child = P, interceptors = I}] -> {ok, P, [], I, 0}; [#connection{hp = {_, _, Interface}, child = P, interceptors = I}] -> {ok, P, [], I, [Interface]} end; connect(Host, Port, SocketType, Timeout, Chars, Wchars, Ctx) when SocketType == ssl -> Key = create_key(Host, Port, Ctx), case ets:lookup(?PM_CONNECTION_DB, Key) of [#connection{child = connecting}] -> SocketOptions = get_ssl_socket_options(Ctx), gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key}, Timeout); [] -> SocketOptions = get_ssl_socket_options(Ctx), gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key}, Timeout); [#connection{hp = {_, _, 0}, child = P, interceptors = I}] -> {ok, P, [], I, 0}; [#connection{hp = {_, _, Interface}, child = P, interceptors = I}] -> {ok, P, [], I, [Interface]} end. get_ssl_socket_options([]) -> SSLOpts = case orber_env:ssl_client_options() of [] -> [{verify, orber_env:ssl_client_verify()}, {depth, orber_env:ssl_client_depth()}, {certfile, orber_env:ssl_client_certfile()}, {cacertfile, orber_env:ssl_client_cacertfile()}, {password, orber_env:ssl_client_password()}, {keyfile, orber_env:ssl_client_keyfile()}, {ciphers, orber_env:ssl_client_ciphers()}, {cachetimeout, orber_env:ssl_client_cachetimeout()}, {keepalive, orber_env:iiop_ssl_out_keepalive()}]; Opts -> case orber_tb:check_illegal_tcp_options(Opts) of ok -> check_old_ssl_client_options([]), Opts; {error, IllegalOpts} -> error_logger:error_report([{application, orber}, "TCP options not allowed to set on a connection", IllegalOpts]), error("Illegal TCP option") end end, ssl_client_extra_options(SSLOpts, []); get_ssl_socket_options([#'IOP_ServiceContext' {context_id=?ORBER_GENERIC_CTX_ID, context_data = {configuration, Options}}|_]) -> SSLOpts = case orber_tb:keysearch(ssl_client_options, Options, orber_env:ssl_client_options()) of [] -> Verify = orber_tb:keysearch(ssl_client_verify, Options, orber_env:ssl_client_verify()), Depth = orber_tb:keysearch(ssl_client_depth, Options, orber_env:ssl_client_depth()), Cert = orber_tb:keysearch(ssl_client_certfile, Options, orber_env:ssl_client_certfile()), CaCert = orber_tb:keysearch(ssl_client_cacertfile, Options, orber_env:ssl_client_cacertfile()), Pwd = orber_tb:keysearch(ssl_client_password, Options, orber_env:ssl_client_password()), Key = orber_tb:keysearch(ssl_client_keyfile, Options, orber_env:ssl_client_keyfile()), Ciphers = orber_tb:keysearch(ssl_client_ciphers, Options, orber_env:ssl_client_ciphers()), Timeout = orber_tb:keysearch(ssl_client_cachetimeout, Options, orber_env:ssl_client_cachetimeout()), KeepAlive = orber_tb:keysearch(ssl_server_cachetimeout, Options, orber_env:iiop_ssl_out_keepalive()), [{verify, Verify}, {depth, Depth}, {certfile, Cert}, {cacertfile, CaCert}, {password, Pwd}, {keyfile, Key}, {ciphers, Ciphers}, {cachetimeout, Timeout}, {keepalive, KeepAlive}]; Opts -> case orber_tb:check_illegal_tcp_options(Opts) of ok -> check_old_ssl_client_options(Options), Opts; {error, IllegalOpts} -> error_logger:error_report([{application, orber}, "TCP options not allowed to set on a connection", IllegalOpts]), error("Illegal TCP option") end end, ssl_client_extra_options(SSLOpts, []); get_ssl_socket_options([_|T]) -> get_ssl_socket_options(T). ssl_client_extra_options([], Acc) -> Acc; ssl_client_extra_options([{_Type, []}|T], Acc) -> ssl_client_extra_options(T, Acc); ssl_client_extra_options([{_Type, infinity}|T], Acc) -> ssl_client_extra_options(T, Acc); ssl_client_extra_options([{Type, Value}|T], Acc) -> ssl_client_extra_options(T, [{Type, Value}|Acc]). add_connection(Key, Key, SockData) -> case ets:lookup(?PM_CONNECTION_DB, Key) of [Connection] -> ets:insert(?PM_CONNECTION_DB, Connection#connection{socketdata = SockData}); [] -> ets:insert(?PM_CONNECTION_DB, #connection{hp= Key, child = connecting, socketdata = SockData}) end; add_connection(Key, NewKey, SockData) -> add_connection(Key, Key, SockData), add_connection(NewKey, NewKey, SockData). get_socket_data(Key) -> case ets:lookup(?PM_CONNECTION_DB, Key) of [#connection{socketdata = SockData}] -> SockData; _ -> {"Unable to extract socket information", 0} end. sockname2peername(SockHost, SockPort) -> orber_tb:unique( do_select([{#connection{hp = {'$1', '$2', '_'}, socketdata = {match_type(SockHost), match_type(SockPort)}, _='_'}, [], [{{'$1', '$2'}}]}])). peername2sockname(PeerHost, PeerPort) -> orber_tb:unique( do_select([{#connection{hp = {match_type(PeerHost), match_type(PeerPort), '_'}, socketdata = '$1', _='_'}, [], ['$1']}])). match_type(0) -> %% Wildcard port number '_'; match_type("") -> %% Wildcard host '_'; match_type(Key) -> %% Wildcard not used. Key. create_key(Host, Port, []) -> {Host, Port, 0}; create_key(Host, Port, [#'IOP_ServiceContext' {context_id=?ORBER_GENERIC_CTX_ID, context_data = {interface, Interface}}|_]) when is_list(Interface) -> {Host, Port, Interface}; create_key(Host, Port, [#'IOP_ServiceContext' {context_id=?ORBER_GENERIC_CTX_ID, context_data = {interface, Interface}}|_]) -> orber:dbg("[~p] orber_iiop_pm:create_key(~p, ~p);~n" "The supplied interface must be a string.", [?LINE, Host, Port, Interface], ?DEBUG_LEVEL), corba:raise(#'BAD_CONTEXT'{completion_status=?COMPLETED_NO}); create_key(Host, Port, [_|T]) -> create_key(Host, Port, T). reconfigure(Options) -> {Local, Proxy} = check_options(Options, [], []), reconfigure_local(Local), reconfigure_proxy(Proxy). reconfigure(Options, Host, Port) -> reconfigure(Options, Host, Port, 0). reconfigure(Options, Host, Port, Interface) -> case ets:lookup(?PM_CONNECTION_DB, {Host, Port, Interface}) of [#connection{child = P}] when is_pid(P) -> case check_options(Options, [], []) of {[], Proxy} -> reconfigure_proxy(Proxy, [P]); {Local, Proxy} -> reconfigure_proxy(Proxy, [P]), gen_server:call(orber_iiop_pm, {reconfigure, Local, Host, Port, Interface}, infinity) end; _ -> {error, "No proxy matched the supplied reference"} end. reconfigure_local([]) -> ok; reconfigure_local(Options) -> gen_server:call(orber_iiop_pm, {reconfigure, Options}, infinity). reconfigure_proxy([]) -> ok; reconfigure_proxy(Options) -> reconfigure_proxy(Options, do_select([{#connection{child = '$1', _='_'}, [], ['$1']}])). reconfigure_proxy(Options, [Pid|T]) -> Pid ! {reconfigure, Options}, reconfigure_proxy(Options, T); reconfigure_proxy(_Options, []) -> ok. check_options([{interceptors, false}|Options], Local, Proxy) -> check_options(Options, [{interceptors, false}|Local], Proxy); check_options([{interceptors, {native, LPIs}}|Options], Local, Proxy) -> check_options(Options, [{interceptors, {native, LPIs}}|Local], Proxy); check_options([{fake, option}|Options], Local, Proxy) -> check_options(Options, Local, [{fake, option}|Proxy]); check_options([_|Options], Local, Proxy) -> check_options(Options, Local, Proxy); check_options([], Local, Proxy) -> {Local, Proxy}. close_connection(PeerData) -> close_connection(PeerData, 0). close_connection(PeerData, Interface) -> gen_server:call(orber_iiop_pm, {disconnect, PeerData, Interface}, infinity). list_existing_connections() -> transform( lists:sort( do_select([{#connection{hp = {'$2','$3','$4'}, child = '$1', _='_'}, [{is_pid, '$1'}], [{{'$1', '$2','$3','$4'}}]}])), []). list_setup_connections() -> transform( lists:sort( do_select([{#connection{hp = {'$1','$2','$3'}, child = connecting, _='_'}, [], [{{'$1','$2','$3'}}]}])), []). list_all_connections() -> transform( lists:sort( do_select([{#connection{hp = {'$2','$3','$4'}, child = '$1', _='_'}, [], [{{'$1','$2','$3', '$4'}}]}])), []). %% Since the connections interface can be 0 or an ip-address we want to %% transform those containing 0. transform([{C, H, P, 0}, {C, H, P, I}|T], Acc) -> %% ACL defined interface. Drop the anonymous one. transform(T, [{H, P, I}|Acc]); transform([{_C, H, P, 0}|T], Acc) -> %% No interface supplied. Drop the 0. transform(T, [{H, P}|Acc]); transform([{_C, H, P, I}|T], Acc) -> %% Interface supplied. Keep it. transform(T, [{H, P, I}|Acc]); transform([{H,P,0}|T], Acc) -> transform(T, [{H,P}|Acc]); transform([{H,P,I}|T], Acc) -> transform(T, [{H,P,I}|Acc]); transform([H|T], Acc) -> transform(T, [H|Acc]); transform([], Acc) -> Acc. do_select(Pattern) -> case catch ets:select(?PM_CONNECTION_DB, Pattern) of {'EXIT', _What} -> []; Result -> Result end. %%----------------------------------------------------------------- %% Internal interface functions %%----------------------------------------------------------------- %%----------------------------------------------------------------- %% Func: stop/0 (Only used for test purpose !!!!!!) %%----------------------------------------------------------------- stop() -> gen_server:call(orber_iiop_pm, stop). %%----------------------------------------------------------------- %% Server functions %%----------------------------------------------------------------- %%----------------------------------------------------------------- %% Func: init/1 %%----------------------------------------------------------------- init(_Opts) -> process_flag(trap_exit, true), {ok, #state{connections = ets:new(orber_iiop_pm_db, [{keypos, 2}, set, public, named_table]), queue = ets:new(orber_iiop_pm_queue, [bag])}}. %%----------------------------------------------------------------- %% Func: terminate/2 %%----------------------------------------------------------------- terminate(_Reason, #state{queue = Q}) -> %% Kill all proxies and close table before terminating stop_all_proxies(ets:first(?PM_CONNECTION_DB)), ets:delete(?PM_CONNECTION_DB), ets:delete(Q), ok. stop_all_proxies('$end_of_table') -> ok; stop_all_proxies(Key) -> case ets:lookup(?PM_CONNECTION_DB, Key) of [] -> ok; [#connection{child = connecting, interceptors = I}] -> invoke_connection_closed(I); [#connection{child = P, interceptors = I}] -> invoke_connection_closed(I), catch orber_iiop_outproxy:stop(P) end, stop_all_proxies(ets:next(?PM_CONNECTION_DB, Key)). %%----------------------------------------------------------------- %% Func: handle_call/3 %%----------------------------------------------------------------- handle_call({connect, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key}, From, State) -> case ets:lookup(?PM_CONNECTION_DB, Key) of [#connection{child = connecting}] -> %% Another client already requested a connection to the given host/port. %% Just add this client to the queue. ets:insert(State#state.queue, {Key, From}), {noreply, State}; [#connection{hp = {_,_,0}, child = P, interceptors = I}] -> %% This case will occur if the PortMapper completed a connection %% between the client's ets:lookup and receiving this request. {reply, {ok, P, [], I, 0}, State}; [#connection{hp = {_,_,Intf}, child = P, interceptors = I}] -> %% This case will occur if the PortMapper completed a connection %% between the client's ets:lookup and receiving this request. {reply, {ok, P, [], I, [Intf]}, State}; [] -> %% The first time a connection is requested to the given host/port. case catch spawn_link(?MODULE, setup_connection, [self(), Host, Port, SocketType, SocketOptions, Chars, Wchars, Key]) of Slave when is_pid(Slave) -> ets:insert(?PM_CONNECTION_DB, #connection{hp = Key, child = connecting, interceptors = false, slave = Slave}), ets:insert(State#state.queue, {Key, From}), {noreply, State}; What -> orber:dbg("[~p] orber_iiop_pm:handle_call(connect);~n" "Unable to invoke setup_connection due to: ~n~p~n", [?LINE, What], ?DEBUG_LEVEL), {reply, {'EXCEPTION', #'INTERNAL'{completion_status=?COMPLETED_NO}}, State} end end; handle_call({disconnect, PeerData, Interface}, _From, State) -> {reply, do_disconnect(PeerData, Interface, State), State}; handle_call({reconfigure, Options, Host, Port, Interface}, _From, State) -> case ets:lookup(?PM_CONNECTION_DB, {Host, Port, Interface}) of [] -> {reply, {error, "No proxy matched the supplied reference"}, State}; [Connection] -> NewConnection = update_connection(Connection, Options), ets:insert(?PM_CONNECTION_DB, NewConnection), {reply, ok, State} end; handle_call({reconfigure, Options}, _From, State) -> case catch update_db(ets:first(?PM_CONNECTION_DB), Options) of ok -> {reply, ok, State}; _What -> {reply, {error, "Unable to change configuration"}, State} end; handle_call(stop, _From, State) -> {stop, normal, ok, State}; handle_call(_, _, State) -> {noreply, State}. update_db('$end_of_table', _) -> ok; update_db(Key, Options) -> [Connection] = ets:lookup(?PM_CONNECTION_DB, Key), NewConnection = update_connection(Connection, Options), ets:insert(?PM_CONNECTION_DB, NewConnection), update_db(ets:next(?PM_CONNECTION_DB, Key), Options). update_connection(Connection, [{interceptors, false}|Options]) -> update_connection(Connection#connection{interceptors = false}, Options); update_connection(#connection{interceptors = false, hp = {PH, PP, _}, socketdata = {SH, SP}} = Connection, [{interceptors, {native, LPIs}}|Options]) -> %% No Interceptor(s). Add the same Ref used by the built in interceptors. update_connection(Connection#connection{interceptors = {native, {PH, PP, SH, SP}, LPIs}}, Options); update_connection(#connection{interceptors = {native, Ref, _}} = Connection, [{interceptors, {native, LPIs}}|Options]) -> %% Interceptor(s) already in use. We must use the same Ref as before. update_connection(Connection#connection{interceptors = {native, Ref, LPIs}}, Options); update_connection(Connection, [H|T]) -> orber:dbg("[~p] orber_iiop_pm:update_connection(~p, ~p)~n" "Unable to update the connection.~n", [?LINE, Connection, H], ?DEBUG_LEVEL), update_connection(Connection, T); update_connection(Connection, []) -> Connection. do_disconnect([], _Interface, _State) -> ok; do_disconnect([{Host, Port}|T], Interface, State) -> case ets:lookup(?PM_CONNECTION_DB, {Host, Port, Interface}) of [] -> ok; [#connection{child = connecting, interceptors = I}] -> ets:delete(?PM_CONNECTION_DB, {Host, Port, Interface}), Exc = {'EXCEPTION',#'INTERNAL'{completion_status = ?COMPLETED_NO}}, send_reply_to_queue(ets:lookup(State#state.queue, {Host, Port, Interface}), Exc), ets:delete(State#state.queue, {Host, Port, Interface}), invoke_connection_closed(I); [#connection{child = P, interceptors = I}] -> unlink(P), catch orber_iiop_outproxy:stop(P), ets:delete(?PM_CONNECTION_DB, {Host, Port, Interface}), invoke_connection_closed(I) end, do_disconnect(T, Interface, State). %%----------------------------------------------------------------- %% Func: handle_cast/2 %%----------------------------------------------------------------- handle_cast(stop, State) -> {stop, normal, State}; handle_cast(_, State) -> {noreply, State}. %%----------------------------------------------------------------- %% Func: handle_info/2 %%----------------------------------------------------------------- %% Trapping exits handle_info({'EXIT', Pid, Reason}, State) -> %% Check the most common scenario first, i.e., a proxy terminates. case ets:match_object(?PM_CONNECTION_DB, #connection{child = Pid, _='_'}) of [#connection{hp = K, interceptors = I}] -> ets:delete(?PM_CONNECTION_DB, K), invoke_connection_closed(I), {noreply, State}; [#connection{hp = K, interceptors = I}, #connection{hp = K2}] -> ets:delete(?PM_CONNECTION_DB, K), ets:delete(?PM_CONNECTION_DB, K2), invoke_connection_closed(I), {noreply, State}; [] when Reason == normal -> %% This might have been a spawned 'setup_connection' which terminated %% after sucessfully setting up a new connection. {noreply, State}; [] -> %% Wasn't a proxy. Hence, we must test if it was a spawned %% 'setup_connection' that failed. case ets:match_object(?PM_CONNECTION_DB, #connection{slave = Pid, _='_'}) of [#connection{hp = K, child = connecting, interceptors = I}] -> ets:delete(?PM_CONNECTION_DB, K), invoke_connection_closed(I), Exc = {'EXCEPTION',#'INTERNAL'{completion_status = ?COMPLETED_NO}}, send_reply_to_queue(ets:lookup(State#state.queue, K), Exc), ets:delete(State#state.queue, K), orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p);~n" "It was not possible to create a connection to the" " given host/port.", [?LINE, K], ?DEBUG_LEVEL), {noreply, State}; [#connection{hp = K, child = connecting, interceptors = I}, #connection{hp = K2}] -> ets:delete(?PM_CONNECTION_DB, K), ets:delete(?PM_CONNECTION_DB, K2), invoke_connection_closed(I), Exc = {'EXCEPTION',#'INTERNAL'{completion_status = ?COMPLETED_NO}}, send_reply_to_queue(ets:lookup(State#state.queue, K), Exc), ets:delete(State#state.queue, K), orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p);~n" "It was not possible to create a connection to the" " given host/port.", [?LINE, K], ?DEBUG_LEVEL), {noreply, State}; _ -> {noreply, State} end end; handle_info({setup_failed, {Host, Port, _} = Key, Key, Exc}, State) -> %% Deletet the data from the connection DB first to avoid clients from %% trying to access it again. ets:delete(?PM_CONNECTION_DB, Key), %% Now we can send whatever exception received. send_reply_to_queue(ets:lookup(State#state.queue, Key), Exc), ets:delete(State#state.queue, Key), orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p ~p);~n" "It was not possible to create a connection to the given host/port.", [?LINE, Host, Port], ?DEBUG_LEVEL), {noreply, State}; handle_info({setup_failed, {Host, Port, _} = Key, NewKey, Exc}, State) -> %% Deletet the data from the connection DB first to avoid clients from %% trying to access it again. ets:delete(?PM_CONNECTION_DB, Key), ets:delete(?PM_CONNECTION_DB, NewKey), %% Now we can send whatever exception received. send_reply_to_queue(ets:lookup(State#state.queue, Key), Exc), ets:delete(State#state.queue, Key), orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p ~p);~n" "It was not possible to create a connection to the given host/port.", [?LINE, Host, Port], ?DEBUG_LEVEL), {noreply, State}; handle_info({setup_successfull, Key, Key, {Child, Ctx, Int}}, State) -> %% Create a link to the proxy and store it in the connection DB. link(Child), case ets:lookup(?PM_CONNECTION_DB, Key) of [Connection] -> ets:insert(?PM_CONNECTION_DB, Connection#connection{hp = Key, child = Child, interceptors = Int, slave = undefined}); [] -> ets:insert(?PM_CONNECTION_DB, #connection{hp = Key, child = Child, interceptors = Int, slave = undefined}) end, %% Send the Proxy reference to all waiting clients. case Key of {_, _, 0} -> send_reply_to_queue(ets:lookup(State#state.queue, Key), {ok, Child, Ctx, Int, 0}); {_, _, Interface} -> send_reply_to_queue(ets:lookup(State#state.queue, Key), {ok, Child, Ctx, Int, [Interface]}) end, %% Reset the queue. ets:delete(State#state.queue, Key), {noreply, State}; handle_info({setup_successfull, Key, NewKey, {Child, Ctx, Int}}, State) -> %% Create a link to the proxy and store it in the connection DB. link(Child), case ets:lookup(?PM_CONNECTION_DB, NewKey) of [Connection] -> ets:insert(?PM_CONNECTION_DB, Connection#connection{hp = NewKey, child = Child, interceptors = Int, slave = undefined}); [] -> ets:insert(?PM_CONNECTION_DB, #connection{hp = NewKey, child = Child, interceptors = Int, slave = undefined}) end, case ets:lookup(?PM_CONNECTION_DB, Key) of [Connection2] -> ets:insert(?PM_CONNECTION_DB, Connection2#connection{hp = Key, child = Child, interceptors = Int, slave = undefined}); [] -> ets:insert(?PM_CONNECTION_DB, #connection{hp = Key, child = Child, interceptors = Int, slave = undefined}) end, %% Send the Proxy reference to all waiting clients. case NewKey of {_, _, 0} -> send_reply_to_queue(ets:lookup(State#state.queue, Key), {ok, Child, Ctx, Int, 0}); {_, _, Interface} -> send_reply_to_queue(ets:lookup(State#state.queue, Key), {ok, Child, Ctx, Int, [Interface]}) end, %% Reset the queue. ets:delete(State#state.queue, Key), {noreply, State}; handle_info(_, State) -> {noreply, State}. send_reply_to_queue([], _) -> ok; send_reply_to_queue([{_, Client}|T], Reply) -> gen_server:reply(Client, Reply), send_reply_to_queue(T, Reply). %%----------------------------------------------------------------- %% Func: code_change/3 %%----------------------------------------------------------------- code_change(_OldVsn, State, _Extra) -> {ok, State}. %%----------------------------------------------------------------- %% Internal functions %%----------------------------------------------------------------- setup_connection(PMPid, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key) -> case catch access_allowed(Host, Port, SocketType, Key) of ok -> do_setup_connection(PMPid, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key, Key); {ok, Interface} -> do_setup_connection(PMPid, Host, Port, SocketType, [{ip, Interface}|SocketOptions], Chars, Wchars, Key, Key); {ok, Interface, NewKey} -> do_setup_connection(PMPid, Host, Port, SocketType, [{ip, Interface}|SocketOptions], Chars, Wchars, Key, NewKey); false -> orber_tb:info("Blocked connect attempt to ~s - ~p", [Host, Port]), PMPid ! {setup_failed, Key, Key, {'EXCEPTION', #'NO_PERMISSION'{completion_status=?COMPLETED_NO}}}, ok; Reason -> orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p); failed~n" "Reason: ~p", [?LINE, Host, Port, Reason], ?DEBUG_LEVEL), PMPid ! {setup_failed, Key, Key, {'EXCEPTION', #'COMM_FAILURE'{completion_status=?COMPLETED_NO}}}, ok end. do_setup_connection(PMPid, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key, NewKey) -> case catch orber_iiop_outsup:connect(Host, Port, SocketType, SocketOptions, PMPid, Key, NewKey) of {error, {'EXCEPTION', E}} -> orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p);~n" "Raised Exc: ~p", [?LINE, Host, Port, E], ?DEBUG_LEVEL), PMPid ! {setup_failed, Key, NewKey, {'EXCEPTION', E}}, ok; {error, Reason} -> orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p);~n" "Got EXIT: ~p", [?LINE, Host, Port, Reason], ?DEBUG_LEVEL), PMPid ! {setup_failed, Key, NewKey, {'EXCEPTION', #'INTERNAL'{completion_status=?COMPLETED_NO}}}, ok; {ok, undefined} -> orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p);~n" "Probably no listener on the given Node/Port or timedout.", [?LINE, Host, Port], ?DEBUG_LEVEL), PMPid ! {setup_failed, Key, NewKey, {'EXCEPTION', #'COMM_FAILURE'{minor=(?ORBER_VMCID bor 1), completion_status=?COMPLETED_NO}}}, ok; {ok, Child} -> case init_interceptors(Host, Port, get_socket_data(Key)) of {'EXCEPTION', E} -> PMPid ! {setup_failed, Key, NewKey, {'EXCEPTION', E}}, ok; Interceptors -> BiDirCtx = orber:bidir_context(), Ctx = case orber:exclude_codeset_ctx() of true -> BiDirCtx; _ -> CodeSetCtx = #'CONV_FRAME_CodeSetContext' {char_data = Chars, wchar_data = Wchars}, [#'IOP_ServiceContext' {context_id=?IOP_CodeSets, context_data = CodeSetCtx} | BiDirCtx] end, PMPid ! {setup_successfull, Key, NewKey, {Child, Ctx, Interceptors}}, ok end end. access_allowed(Host, Port, Type, {_,_,UserInterface}) -> Flags = orber:get_flags(), Family = orber_env:ip_version(), case ?ORB_FLAG_TEST(Flags, ?ORB_ENV_USE_ACL_OUTGOING) of false when UserInterface == 0 -> get_local_interface(Type, Family); false -> inet:getaddr(UserInterface, Family); true -> SearchFor = case Type of normal -> tcp_out; ssl -> ssl_out end, {ok, Ip} = inet:getaddr(Host, Family), case orber_acl:match(Ip, SearchFor, true) of {true, [], 0} -> get_local_interface(Type, Family); {true, [], Port} -> get_local_interface(Type, Family); {true, [], {Min, Max}} when Port >= Min, Port =< Max -> get_local_interface(Type, Family); {true, [Interface], 0} -> {ok, NewIp} = inet:getaddr(Interface, Family), {ok, NewIp, {Host, Port, 0}}; {true, [Interface], Port} -> {ok, NewIp} = inet:getaddr(Interface, Family), {ok, NewIp, {Host, Port, 0}}; {true, [Interface], {Min, Max}} when Port >= Min, Port =< Max -> {ok, NewIp} = inet:getaddr(Interface, Family), {ok, NewIp, {Host, Port, 0}}; _ -> false end end. get_local_interface(normal, Family) -> case orber_env:ip_address_local() of [] -> ok; [Interface] -> inet:getaddr(Interface, Family) end; get_local_interface(ssl, Family) -> case orber_env:iiop_ssl_ip_address_local() of [] -> ok; [Interface] -> inet:getaddr(Interface, Family) end. invoke_connection_closed(false) -> ok; invoke_connection_closed({native, Ref, PIs}) -> (catch orber_pi:closed_out_connection(PIs, Ref)); invoke_connection_closed({_Type, _PIs}) -> ok. init_interceptors(Host, Port, {SHost, SPort}) -> case orber:get_interceptors() of {native, PIs} -> case catch orber_pi:new_out_connection(PIs, Host, Port, SHost, SPort) of {'EXIT', R} -> orber:dbg("[~p] orber_iiop_pm:init_interceptors(~p); Got Exit: ~p.~n" "One or more Interceptor incorrect or undefined?", [?LINE, PIs, R], ?DEBUG_LEVEL), {'EXCEPTION', #'COMM_FAILURE'{minor=(?ORBER_VMCID bor 2), completion_status=?COMPLETED_NO}}; IntRef -> {native, IntRef, PIs} end; Other -> %% Either 'false' or {Type, PIs}. Other end. check_old_ssl_client_options(Options) -> try 0 = orber_tb:keysearch(ssl_client_verify, Options, orber_env:ssl_client_verify()), 1 = orber_tb:keysearch(ssl_client_depth, Options, orber_env:ssl_client_depth()), [] = orber_tb:keysearch(ssl_client_certfile, Options, orber_env:ssl_client_certfile()), [] = orber_tb:keysearch(ssl_client_cacertfile, Options, orber_env:ssl_client_cacertfile()), [] = orber_tb:keysearch(ssl_client_password, Options, orber_env:ssl_client_password()), [] = orber_tb:keysearch(ssl_client_keyfile, Options, orber_env:ssl_client_keyfile()), [] = orber_tb:keysearch(ssl_client_ciphers, Options, orber_env:ssl_client_ciphers()), infinity = orber_tb:keysearch(ssl_client_cachetimeout, Options, orber_env:ssl_client_cachetimeout()), false = orber_tb:keysearch(iiop_ssl_out_keepalive, Options, orber_env:iiop_ssl_out_keepalive()) catch _:_ -> error_logger:warning_report([{application, orber}, "Ignoring deprecated ssl client options used together with the ssl_client_options"]) end. %%----------------------------------------------------------------- %% END OF MODULE %%-----------------------------------------------------------------