1%%--------------------------------------------------------------------
2%%
3%% %CopyrightBegin%
4%%
5%% Copyright Ericsson AB 1999-2016. All Rights Reserved.
6%%
7%% Licensed under the Apache License, Version 2.0 (the "License");
8%% you may not use this file except in compliance with the License.
9%% You may obtain a copy of the License at
10%%
11%%     http://www.apache.org/licenses/LICENSE-2.0
12%%
13%% Unless required by applicable law or agreed to in writing, software
14%% distributed under the License is distributed on an "AS IS" BASIS,
15%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16%% See the License for the specific language governing permissions and
17%% limitations under the License.
18%%
19%% %CopyrightEnd%
20%%
21%%
22%%-----------------------------------------------------------------
23%% File: orber_iiop_pm.erl
24%% Description:
25%%    This file contains the mapping of addresses on the format {Host, Port}
26%%    to a proxy pid.
27%%
28%%-----------------------------------------------------------------
29-module(orber_iiop_pm).
30
31-behaviour(gen_server).
32
33-include_lib("orber/src/orber_iiop.hrl").
34-include_lib("orber/include/corba.hrl").
35-include_lib("kernel/include/inet.hrl").
36
37%%-----------------------------------------------------------------
38%% External exports
39%%-----------------------------------------------------------------
40-export([start/0, start/1]).
41
42%%-----------------------------------------------------------------
43%% Internal exports
44%%-----------------------------------------------------------------
45-export([connect/7,
46	 close_connection/1, close_connection/2,
47	 list_existing_connections/0,
48	 list_setup_connections/0,
49	 list_all_connections/0,
50	 init/1, handle_call/3, handle_cast/2, handle_info/2,
51	 code_change/3, terminate/2, stop/0, setup_connection/8,
52	 reconfigure/1, reconfigure/3, reconfigure/4, add_connection/3,
53	 sockname2peername/2, peername2sockname/2]).
54
55%%-----------------------------------------------------------------
56%% Macros/Defines
57%%-----------------------------------------------------------------
58-define(DEBUG_LEVEL, 7).
59
60-define(PM_CONNECTION_DB, orber_iiop_pm_db).
61
62-record(state, {connections, queue}).
63
64-record(connection, {hp, child, interceptors, slave,
65		     flags = 0, alias = 0, socketdata = {"Unavailable", 0}}).
66
67%%-----------------------------------------------------------------
68%% External interface functions
69%%-----------------------------------------------------------------
70start() ->
71    ignore.
72start(Opts) ->
73    gen_server:start_link({local, 'orber_iiop_pm'}, ?MODULE, Opts, []).
74
75
76connect(Host, Port, SocketType, Timeout, Chars, Wchars, Ctx)
77  when SocketType == normal ->
78    Key = create_key(Host, Port, Ctx),
79    case ets:lookup(?PM_CONNECTION_DB, Key) of
80	[#connection{child = connecting}] ->
81	    gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType,
82					    [], Chars, Wchars, Key}, Timeout);
83	[] ->
84	    gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType,
85					    [], Chars, Wchars, Key}, Timeout);
86	[#connection{hp = {_, _, 0}, child = P, interceptors = I}] ->
87	    {ok, P, [], I, 0};
88	[#connection{hp = {_, _, Interface}, child = P, interceptors = I}] ->
89	    {ok, P, [], I, [Interface]}
90    end;
91connect(Host, Port, SocketType, Timeout, Chars, Wchars, Ctx)
92  when SocketType == ssl ->
93    Key = create_key(Host, Port, Ctx),
94    case ets:lookup(?PM_CONNECTION_DB, Key) of
95	[#connection{child = connecting}] ->
96	    SocketOptions = get_ssl_socket_options(Ctx),
97	    gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType,
98					    SocketOptions, Chars, Wchars, Key},
99			    Timeout);
100	[] ->
101	    SocketOptions = get_ssl_socket_options(Ctx),
102	    gen_server:call(orber_iiop_pm, {connect, Host, Port, SocketType,
103					    SocketOptions, Chars, Wchars, Key},
104			    Timeout);
105	[#connection{hp = {_, _, 0}, child = P, interceptors = I}] ->
106	    {ok, P, [], I, 0};
107	[#connection{hp = {_, _, Interface}, child = P, interceptors = I}] ->
108	    {ok, P, [], I, [Interface]}
109    end.
110
111get_ssl_socket_options([]) ->
112    SSLOpts =
113	case orber_env:ssl_client_options() of
114	    [] ->
115		[{verify, orber_env:ssl_client_verify()},
116		 {depth, orber_env:ssl_client_depth()},
117		 {certfile, orber_env:ssl_client_certfile()},
118		 {cacertfile, orber_env:ssl_client_cacertfile()},
119		 {password, orber_env:ssl_client_password()},
120		 {keyfile, orber_env:ssl_client_keyfile()},
121		 {ciphers, orber_env:ssl_client_ciphers()},
122		 {cachetimeout, orber_env:ssl_client_cachetimeout()},
123		 {keepalive, orber_env:iiop_ssl_out_keepalive()}];
124	    Opts ->
125		case orber_tb:check_illegal_tcp_options(Opts) of
126		    ok ->
127			check_old_ssl_client_options([]),
128			Opts;
129		    {error, IllegalOpts} ->
130			error_logger:error_report([{application, orber},
131						   "TCP options not allowed to set on a connection",
132						   IllegalOpts]),
133			error("Illegal TCP option")
134		end
135	end,
136    ssl_client_extra_options(SSLOpts, []);
137get_ssl_socket_options([#'IOP_ServiceContext'
138			{context_id=?ORBER_GENERIC_CTX_ID,
139			 context_data = {configuration, Options}}|_]) ->
140    SSLOpts =
141	case orber_tb:keysearch(ssl_client_options, Options,
142				orber_env:ssl_client_options()) of
143	    [] ->
144		Verify = orber_tb:keysearch(ssl_client_verify, Options,
145					    orber_env:ssl_client_verify()),
146		Depth = orber_tb:keysearch(ssl_client_depth, Options,
147					   orber_env:ssl_client_depth()),
148		Cert = orber_tb:keysearch(ssl_client_certfile, Options,
149					  orber_env:ssl_client_certfile()),
150		CaCert = orber_tb:keysearch(ssl_client_cacertfile, Options,
151					    orber_env:ssl_client_cacertfile()),
152		Pwd = orber_tb:keysearch(ssl_client_password, Options,
153					 orber_env:ssl_client_password()),
154		Key = orber_tb:keysearch(ssl_client_keyfile, Options,
155					 orber_env:ssl_client_keyfile()),
156		Ciphers = orber_tb:keysearch(ssl_client_ciphers, Options,
157					     orber_env:ssl_client_ciphers()),
158		Timeout = orber_tb:keysearch(ssl_client_cachetimeout, Options,
159					     orber_env:ssl_client_cachetimeout()),
160		KeepAlive = orber_tb:keysearch(ssl_server_cachetimeout, Options,
161					       orber_env:iiop_ssl_out_keepalive()),
162		[{verify, Verify},
163		 {depth, Depth},
164		 {certfile, Cert},
165		 {cacertfile, CaCert},
166		 {password, Pwd},
167		 {keyfile, Key},
168		 {ciphers, Ciphers},
169		 {cachetimeout, Timeout},
170		 {keepalive, KeepAlive}];
171	    Opts ->
172		case orber_tb:check_illegal_tcp_options(Opts) of
173		    ok ->
174			check_old_ssl_client_options(Options),
175			Opts;
176		    {error, IllegalOpts} ->
177			error_logger:error_report([{application, orber},
178						   "TCP options not allowed to set on a connection",
179						   IllegalOpts]),
180			error("Illegal TCP option")
181		end
182	end,
183    ssl_client_extra_options(SSLOpts, []);
184get_ssl_socket_options([_|T]) ->
185    get_ssl_socket_options(T).
186
187
188ssl_client_extra_options([], Acc) ->
189    Acc;
190ssl_client_extra_options([{_Type, []}|T], Acc) ->
191    ssl_client_extra_options(T, Acc);
192ssl_client_extra_options([{_Type, infinity}|T], Acc) ->
193    ssl_client_extra_options(T, Acc);
194ssl_client_extra_options([{Type, Value}|T], Acc) ->
195    ssl_client_extra_options(T, [{Type, Value}|Acc]).
196
197add_connection(Key, Key, SockData) ->
198    case ets:lookup(?PM_CONNECTION_DB, Key) of
199	[Connection] ->
200	    ets:insert(?PM_CONNECTION_DB,
201		       Connection#connection{socketdata = SockData});
202	[] ->
203	    ets:insert(?PM_CONNECTION_DB,
204		       #connection{hp= Key, child = connecting,
205				   socketdata = SockData})
206    end;
207add_connection(Key, NewKey, SockData) ->
208    add_connection(Key, Key, SockData),
209    add_connection(NewKey, NewKey, SockData).
210
211get_socket_data(Key) ->
212    case ets:lookup(?PM_CONNECTION_DB, Key) of
213	[#connection{socketdata = SockData}] ->
214	    SockData;
215	_ ->
216	    {"Unable to extract socket information", 0}
217    end.
218
219sockname2peername(SockHost, SockPort) ->
220    orber_tb:unique(
221      do_select([{#connection{hp = {'$1', '$2', '_'},
222			      socketdata = {match_type(SockHost),
223					    match_type(SockPort)},
224			      _='_'}, [], [{{'$1', '$2'}}]}])).
225
226
227peername2sockname(PeerHost, PeerPort) ->
228    orber_tb:unique(
229      do_select([{#connection{hp = {match_type(PeerHost),
230				    match_type(PeerPort),
231				    '_'},
232			      socketdata = '$1',
233			      _='_'}, [], ['$1']}])).
234
235match_type(0) ->
236    %% Wildcard port number
237    '_';
238match_type("") ->
239    %% Wildcard host
240    '_';
241match_type(Key) ->
242    %% Wildcard not used.
243    Key.
244
245create_key(Host, Port, []) ->
246    {Host, Port, 0};
247create_key(Host, Port,
248	   [#'IOP_ServiceContext'
249	    {context_id=?ORBER_GENERIC_CTX_ID,
250	     context_data = {interface, Interface}}|_]) when is_list(Interface) ->
251    {Host, Port, Interface};
252create_key(Host, Port,
253	   [#'IOP_ServiceContext'
254	    {context_id=?ORBER_GENERIC_CTX_ID,
255	     context_data = {interface, Interface}}|_]) ->
256    orber:dbg("[~p] orber_iiop_pm:create_key(~p, ~p);~n"
257	      "The supplied interface must be a string.",
258	      [?LINE, Host, Port, Interface], ?DEBUG_LEVEL),
259    corba:raise(#'BAD_CONTEXT'{completion_status=?COMPLETED_NO});
260create_key(Host, Port, [_|T]) ->
261    create_key(Host, Port, T).
262
263reconfigure(Options) ->
264    {Local, Proxy} = check_options(Options, [], []),
265    reconfigure_local(Local),
266    reconfigure_proxy(Proxy).
267
268
269reconfigure(Options, Host, Port) ->
270    reconfigure(Options, Host, Port, 0).
271reconfigure(Options, Host, Port, Interface) ->
272    case ets:lookup(?PM_CONNECTION_DB, {Host, Port, Interface}) of
273	[#connection{child = P}] when is_pid(P) ->
274	    case check_options(Options, [], []) of
275		{[], Proxy} ->
276		    reconfigure_proxy(Proxy, [P]);
277		{Local, Proxy} ->
278		    reconfigure_proxy(Proxy, [P]),
279		    gen_server:call(orber_iiop_pm, {reconfigure, Local,
280						    Host, Port, Interface}, infinity)
281	    end;
282	_ ->
283	    {error, "No proxy matched the supplied reference"}
284    end.
285
286reconfigure_local([]) ->
287    ok;
288reconfigure_local(Options) ->
289    gen_server:call(orber_iiop_pm, {reconfigure, Options}, infinity).
290
291reconfigure_proxy([]) ->
292    ok;
293reconfigure_proxy(Options) ->
294    reconfigure_proxy(Options, do_select([{#connection{child = '$1', _='_'},
295					   [], ['$1']}])).
296
297reconfigure_proxy(Options, [Pid|T]) ->
298    Pid ! {reconfigure, Options},
299    reconfigure_proxy(Options, T);
300reconfigure_proxy(_Options, []) ->
301    ok.
302
303
304check_options([{interceptors, false}|Options], Local, Proxy) ->
305    check_options(Options, [{interceptors, false}|Local], Proxy);
306check_options([{interceptors, {native, LPIs}}|Options], Local, Proxy) ->
307    check_options(Options, [{interceptors, {native, LPIs}}|Local], Proxy);
308check_options([{fake, option}|Options], Local, Proxy) ->
309    check_options(Options, Local, [{fake, option}|Proxy]);
310check_options([_|Options], Local, Proxy) ->
311    check_options(Options, Local, Proxy);
312check_options([], Local, Proxy) ->
313    {Local, Proxy}.
314
315
316close_connection(PeerData) ->
317    close_connection(PeerData, 0).
318
319close_connection(PeerData, Interface) ->
320    gen_server:call(orber_iiop_pm, {disconnect, PeerData, Interface}, infinity).
321
322
323list_existing_connections() ->
324    transform(
325      lists:sort(
326	do_select([{#connection{hp = {'$2','$3','$4'}, child = '$1', _='_'},
327		    [{is_pid, '$1'}], [{{'$1', '$2','$3','$4'}}]}])), []).
328
329list_setup_connections() ->
330    transform(
331      lists:sort(
332	do_select([{#connection{hp = {'$1','$2','$3'}, child = connecting, _='_'}, [],
333			     [{{'$1','$2','$3'}}]}])), []).
334
335list_all_connections() ->
336    transform(
337      lists:sort(
338	do_select([{#connection{hp = {'$2','$3','$4'}, child = '$1', _='_'}, [],
339		    [{{'$1','$2','$3', '$4'}}]}])), []).
340
341%% Since the connections interface can be 0 or an ip-address we want to
342%% transform those containing 0.
343transform([{C, H, P, 0}, {C, H, P, I}|T], Acc) ->
344    %% ACL defined interface. Drop the anonymous one.
345    transform(T, [{H, P, I}|Acc]);
346transform([{_C, H, P, 0}|T], Acc) ->
347    %% No interface supplied. Drop the 0.
348    transform(T, [{H, P}|Acc]);
349transform([{_C, H, P, I}|T], Acc) ->
350    %% Interface supplied. Keep it.
351    transform(T, [{H, P, I}|Acc]);
352transform([{H,P,0}|T], Acc) ->
353    transform(T, [{H,P}|Acc]);
354transform([{H,P,I}|T], Acc) ->
355    transform(T, [{H,P,I}|Acc]);
356transform([H|T], Acc) ->
357    transform(T, [H|Acc]);
358transform([], Acc) ->
359    Acc.
360
361do_select(Pattern) ->
362    case catch ets:select(?PM_CONNECTION_DB, Pattern) of
363	{'EXIT', _What} ->
364	    [];
365	Result ->
366	    Result
367    end.
368
369%%-----------------------------------------------------------------
370%% Internal interface functions
371%%-----------------------------------------------------------------
372%%-----------------------------------------------------------------
373%% Func: stop/0 (Only used for test purpose !!!!!!)
374%%-----------------------------------------------------------------
375stop() ->
376    gen_server:call(orber_iiop_pm, stop).
377
378%%-----------------------------------------------------------------
379%% Server functions
380%%-----------------------------------------------------------------
381%%-----------------------------------------------------------------
382%% Func: init/1
383%%-----------------------------------------------------------------
384init(_Opts) ->
385    process_flag(trap_exit, true),
386    {ok, #state{connections = ets:new(orber_iiop_pm_db,
387				      [{keypos, 2}, set, public, named_table]),
388		queue = ets:new(orber_iiop_pm_queue, [bag])}}.
389
390%%-----------------------------------------------------------------
391%% Func: terminate/2
392%%-----------------------------------------------------------------
393terminate(_Reason, #state{queue = Q}) ->
394    %% Kill all proxies and close table before terminating
395    stop_all_proxies(ets:first(?PM_CONNECTION_DB)),
396    ets:delete(?PM_CONNECTION_DB),
397    ets:delete(Q),
398    ok.
399
400stop_all_proxies('$end_of_table') ->
401    ok;
402stop_all_proxies(Key) ->
403    case ets:lookup(?PM_CONNECTION_DB, Key) of
404	[] ->
405	    ok;
406	[#connection{child = connecting, interceptors = I}] ->
407	    invoke_connection_closed(I);
408	[#connection{child = P, interceptors = I}] ->
409	    invoke_connection_closed(I),
410	    catch orber_iiop_outproxy:stop(P)
411    end,
412    stop_all_proxies(ets:next(?PM_CONNECTION_DB, Key)).
413
414%%-----------------------------------------------------------------
415%% Func: handle_call/3
416%%-----------------------------------------------------------------
417handle_call({connect, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key},
418	    From, State) ->
419    case ets:lookup(?PM_CONNECTION_DB, Key) of
420	[#connection{child = connecting}] ->
421	    %% Another client already requested a connection to the given host/port.
422	    %% Just add this client to the queue.
423	    ets:insert(State#state.queue, {Key, From}),
424	    {noreply, State};
425	[#connection{hp = {_,_,0}, child = P, interceptors = I}] ->
426	    %% This case will occur if the PortMapper completed a connection
427	    %% between the client's ets:lookup and receiving this request.
428	    {reply, {ok, P, [], I, 0}, State};
429	[#connection{hp = {_,_,Intf}, child = P, interceptors = I}] ->
430	    %% This case will occur if the PortMapper completed a connection
431	    %% between the client's ets:lookup and receiving this request.
432	    {reply, {ok, P, [], I, [Intf]}, State};
433	[] ->
434	    %% The first time a connection is requested to the given host/port.
435	    case catch spawn_link(?MODULE, setup_connection,
436				  [self(), Host, Port, SocketType,
437				   SocketOptions, Chars, Wchars, Key]) of
438		Slave when is_pid(Slave) ->
439		    ets:insert(?PM_CONNECTION_DB,
440			       #connection{hp = Key, child = connecting,
441					   interceptors = false, slave = Slave}),
442		    ets:insert(State#state.queue, {Key, From}),
443		    {noreply, State};
444		What ->
445		    orber:dbg("[~p] orber_iiop_pm:handle_call(connect);~n"
446			      "Unable to invoke setup_connection due to: ~n~p~n",
447			      [?LINE, What], ?DEBUG_LEVEL),
448		    {reply,
449		     {'EXCEPTION', #'INTERNAL'{completion_status=?COMPLETED_NO}},
450		     State}
451	    end
452    end;
453handle_call({disconnect, PeerData, Interface}, _From, State) ->
454    {reply, do_disconnect(PeerData, Interface, State), State};
455handle_call({reconfigure, Options, Host, Port, Interface},
456	    _From, State) ->
457    case ets:lookup(?PM_CONNECTION_DB, {Host, Port, Interface}) of
458	[] ->
459	    {reply, {error, "No proxy matched the supplied reference"}, State};
460	[Connection] ->
461	    NewConnection = update_connection(Connection, Options),
462	    ets:insert(?PM_CONNECTION_DB, NewConnection),
463	    {reply, ok, State}
464    end;
465handle_call({reconfigure, Options}, _From, State) ->
466    case catch update_db(ets:first(?PM_CONNECTION_DB), Options) of
467	ok ->
468	    {reply, ok, State};
469	_What ->
470	    {reply, {error, "Unable to change configuration"}, State}
471    end;
472handle_call(stop, _From, State) ->
473    {stop, normal, ok, State};
474handle_call(_, _, State) ->
475    {noreply, State}.
476
477update_db('$end_of_table', _) ->
478    ok;
479update_db(Key, Options) ->
480    [Connection] = ets:lookup(?PM_CONNECTION_DB, Key),
481    NewConnection = update_connection(Connection, Options),
482    ets:insert(?PM_CONNECTION_DB, NewConnection),
483    update_db(ets:next(?PM_CONNECTION_DB, Key), Options).
484
485
486update_connection(Connection, [{interceptors, false}|Options]) ->
487    update_connection(Connection#connection{interceptors = false}, Options);
488update_connection(#connection{interceptors = false,
489			      hp = {PH, PP, _},
490			      socketdata = {SH, SP}} = Connection,
491		  [{interceptors, {native, LPIs}}|Options]) ->
492    %% No Interceptor(s). Add the same Ref used by the built in interceptors.
493    update_connection(Connection#connection{interceptors =
494					    {native, {PH, PP, SH, SP}, LPIs}},
495		      Options);
496update_connection(#connection{interceptors = {native, Ref, _}} = Connection,
497		  [{interceptors, {native, LPIs}}|Options]) ->
498    %% Interceptor(s) already in use. We must use the same Ref as before.
499    update_connection(Connection#connection{interceptors =
500					    {native, Ref, LPIs}},
501		      Options);
502update_connection(Connection, [H|T]) ->
503    orber:dbg("[~p] orber_iiop_pm:update_connection(~p, ~p)~n"
504	      "Unable to update the connection.~n",
505	      [?LINE, Connection, H], ?DEBUG_LEVEL),
506    update_connection(Connection, T);
507update_connection(Connection, []) ->
508    Connection.
509
510do_disconnect([], _Interface, _State) ->
511    ok;
512do_disconnect([{Host, Port}|T], Interface, State) ->
513    case ets:lookup(?PM_CONNECTION_DB, {Host, Port, Interface}) of
514	[] ->
515	    ok;
516	[#connection{child = connecting, interceptors = I}] ->
517	    ets:delete(?PM_CONNECTION_DB, {Host, Port, Interface}),
518	    Exc = {'EXCEPTION',#'INTERNAL'{completion_status = ?COMPLETED_NO}},
519	    send_reply_to_queue(ets:lookup(State#state.queue,
520					   {Host, Port, Interface}), Exc),
521	    ets:delete(State#state.queue, {Host, Port, Interface}),
522	    invoke_connection_closed(I);
523	[#connection{child = P, interceptors = I}] ->
524	    unlink(P),
525	    catch orber_iiop_outproxy:stop(P),
526	    ets:delete(?PM_CONNECTION_DB, {Host, Port, Interface}),
527	    invoke_connection_closed(I)
528    end,
529    do_disconnect(T, Interface, State).
530
531%%-----------------------------------------------------------------
532%% Func: handle_cast/2
533%%-----------------------------------------------------------------
534handle_cast(stop, State) ->
535    {stop, normal, State};
536handle_cast(_, State) ->
537    {noreply, State}.
538
539%%-----------------------------------------------------------------
540%% Func: handle_info/2
541%%-----------------------------------------------------------------
542%% Trapping exits
543handle_info({'EXIT', Pid, Reason}, State) ->
544    %% Check the most common scenario first, i.e., a proxy terminates.
545    case ets:match_object(?PM_CONNECTION_DB, #connection{child = Pid, _='_'}) of
546	[#connection{hp = K, interceptors = I}] ->
547	    ets:delete(?PM_CONNECTION_DB, K),
548	    invoke_connection_closed(I),
549	    {noreply, State};
550	[#connection{hp = K, interceptors = I}, #connection{hp = K2}] ->
551	    ets:delete(?PM_CONNECTION_DB, K),
552	    ets:delete(?PM_CONNECTION_DB, K2),
553	    invoke_connection_closed(I),
554	    {noreply, State};
555	[] when Reason == normal ->
556	    %% This might have been a spawned 'setup_connection' which terminated
557	    %% after sucessfully setting up a new connection.
558	    {noreply, State};
559	[] ->
560	    %% Wasn't a proxy. Hence, we must test if it was a spawned
561	    %% 'setup_connection' that failed.
562	    case ets:match_object(?PM_CONNECTION_DB, #connection{slave = Pid, _='_'}) of
563		[#connection{hp = K, child = connecting, interceptors = I}] ->
564		    ets:delete(?PM_CONNECTION_DB, K),
565		    invoke_connection_closed(I),
566		    Exc = {'EXCEPTION',#'INTERNAL'{completion_status = ?COMPLETED_NO}},
567		    send_reply_to_queue(ets:lookup(State#state.queue, K), Exc),
568		    ets:delete(State#state.queue, K),
569		    orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p);~n"
570			      "It was not possible to create a connection to the"
571			      " given host/port.",
572			      [?LINE, K], ?DEBUG_LEVEL),
573		    {noreply, State};
574		[#connection{hp = K, child = connecting, interceptors = I},
575		 #connection{hp = K2}] ->
576		    ets:delete(?PM_CONNECTION_DB, K),
577		    ets:delete(?PM_CONNECTION_DB, K2),
578		    invoke_connection_closed(I),
579		    Exc = {'EXCEPTION',#'INTERNAL'{completion_status = ?COMPLETED_NO}},
580		    send_reply_to_queue(ets:lookup(State#state.queue, K), Exc),
581		    ets:delete(State#state.queue, K),
582		    orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p);~n"
583			      "It was not possible to create a connection to the"
584			      " given host/port.",
585			      [?LINE, K], ?DEBUG_LEVEL),
586		    {noreply, State};
587		_ ->
588		    {noreply, State}
589	    end
590    end;
591handle_info({setup_failed, {Host, Port, _} = Key, Key, Exc}, State) ->
592    %% Deletet the data from the connection DB first to avoid clients from
593    %% trying to access it again.
594    ets:delete(?PM_CONNECTION_DB, Key),
595    %% Now we can send whatever exception received.
596    send_reply_to_queue(ets:lookup(State#state.queue, Key), Exc),
597    ets:delete(State#state.queue, Key),
598    orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p ~p);~n"
599	      "It was not possible to create a connection to the given host/port.",
600	      [?LINE, Host, Port], ?DEBUG_LEVEL),
601    {noreply, State};
602handle_info({setup_failed, {Host, Port, _} = Key, NewKey, Exc}, State) ->
603    %% Deletet the data from the connection DB first to avoid clients from
604    %% trying to access it again.
605    ets:delete(?PM_CONNECTION_DB, Key),
606    ets:delete(?PM_CONNECTION_DB, NewKey),
607    %% Now we can send whatever exception received.
608    send_reply_to_queue(ets:lookup(State#state.queue, Key), Exc),
609    ets:delete(State#state.queue, Key),
610    orber:dbg("[~p] orber_iiop_pm:handle_info(setup_failed ~p ~p);~n"
611	      "It was not possible to create a connection to the given host/port.",
612	      [?LINE, Host, Port], ?DEBUG_LEVEL),
613    {noreply, State};
614handle_info({setup_successfull, Key, Key, {Child, Ctx, Int}}, State) ->
615    %% Create a link to the proxy and store it in the connection DB.
616    link(Child),
617    case ets:lookup(?PM_CONNECTION_DB, Key) of
618	[Connection] ->
619	    ets:insert(?PM_CONNECTION_DB,
620		       Connection#connection{hp = Key, child = Child,
621					     interceptors = Int,
622					     slave = undefined});
623	[] ->
624	    ets:insert(?PM_CONNECTION_DB,
625		       #connection{hp = Key, child = Child,
626				   interceptors = Int,
627				   slave = undefined})
628    end,
629    %% Send the Proxy reference to all waiting clients.
630    case Key of
631	{_, _, 0} ->
632	    send_reply_to_queue(ets:lookup(State#state.queue, Key),
633				{ok, Child, Ctx, Int, 0});
634	{_, _, Interface} ->
635	    send_reply_to_queue(ets:lookup(State#state.queue, Key),
636				{ok, Child, Ctx, Int, [Interface]})
637    end,
638    %% Reset the queue.
639    ets:delete(State#state.queue, Key),
640    {noreply, State};
641handle_info({setup_successfull, Key, NewKey, {Child, Ctx, Int}}, State) ->
642    %% Create a link to the proxy and store it in the connection DB.
643    link(Child),
644    case ets:lookup(?PM_CONNECTION_DB, NewKey) of
645	[Connection] ->
646	    ets:insert(?PM_CONNECTION_DB,
647		       Connection#connection{hp = NewKey, child = Child,
648					     interceptors = Int,
649					     slave = undefined});
650	[] ->
651	    ets:insert(?PM_CONNECTION_DB,
652		       #connection{hp = NewKey, child = Child,
653				   interceptors = Int,
654				   slave = undefined})
655    end,
656    case ets:lookup(?PM_CONNECTION_DB, Key) of
657	[Connection2] ->
658	    ets:insert(?PM_CONNECTION_DB,
659		       Connection2#connection{hp = Key, child = Child,
660					     interceptors = Int,
661					     slave = undefined});
662	[] ->
663	    ets:insert(?PM_CONNECTION_DB,
664		       #connection{hp = Key, child = Child,
665				   interceptors = Int,
666				   slave = undefined})
667    end,
668    %% Send the Proxy reference to all waiting clients.
669    case NewKey of
670	{_, _, 0} ->
671	    send_reply_to_queue(ets:lookup(State#state.queue, Key),
672				{ok, Child, Ctx, Int, 0});
673	{_, _, Interface} ->
674	    send_reply_to_queue(ets:lookup(State#state.queue, Key),
675				{ok, Child, Ctx, Int, [Interface]})
676    end,
677    %% Reset the queue.
678    ets:delete(State#state.queue, Key),
679    {noreply, State};
680handle_info(_, State) ->
681    {noreply, State}.
682
683
684send_reply_to_queue([], _) ->
685    ok;
686send_reply_to_queue([{_, Client}|T], Reply) ->
687    gen_server:reply(Client, Reply),
688    send_reply_to_queue(T, Reply).
689
690%%-----------------------------------------------------------------
691%% Func: code_change/3
692%%-----------------------------------------------------------------
693code_change(_OldVsn, State, _Extra) ->
694    {ok, State}.
695
696%%-----------------------------------------------------------------
697%% Internal functions
698%%-----------------------------------------------------------------
699setup_connection(PMPid, Host, Port, SocketType, SocketOptions, Chars, Wchars, Key) ->
700    case catch access_allowed(Host, Port, SocketType, Key) of
701	ok  ->
702	    do_setup_connection(PMPid, Host, Port, SocketType, SocketOptions,
703				Chars, Wchars, Key, Key);
704	{ok, Interface} ->
705	    do_setup_connection(PMPid, Host, Port, SocketType,
706				[{ip, Interface}|SocketOptions],
707				Chars, Wchars, Key, Key);
708	{ok, Interface, NewKey} ->
709	    do_setup_connection(PMPid, Host, Port, SocketType,
710				[{ip, Interface}|SocketOptions],
711				Chars, Wchars, Key, NewKey);
712	false ->
713	    orber_tb:info("Blocked connect attempt to ~s - ~p", [Host, Port]),
714	    PMPid ! {setup_failed, Key, Key,
715		     {'EXCEPTION', #'NO_PERMISSION'{completion_status=?COMPLETED_NO}}},
716	    ok;
717	Reason ->
718	    orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p); failed~n"
719		      "Reason: ~p",
720		      [?LINE, Host, Port, Reason], ?DEBUG_LEVEL),
721	    PMPid ! {setup_failed, Key, Key,
722		     {'EXCEPTION', #'COMM_FAILURE'{completion_status=?COMPLETED_NO}}},
723	    ok
724    end.
725
726
727do_setup_connection(PMPid, Host, Port, SocketType, SocketOptions, Chars,
728		    Wchars, Key, NewKey) ->
729    case catch orber_iiop_outsup:connect(Host, Port, SocketType,
730					 SocketOptions, PMPid, Key, NewKey) of
731	{error, {'EXCEPTION', E}} ->
732	    orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p);~n"
733		      "Raised Exc: ~p",
734		      [?LINE, Host, Port, E], ?DEBUG_LEVEL),
735	    PMPid ! {setup_failed, Key, NewKey, {'EXCEPTION', E}},
736	    ok;
737	{error, Reason} ->
738	    orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p);~n"
739		      "Got EXIT: ~p",
740		      [?LINE, Host, Port, Reason], ?DEBUG_LEVEL),
741	    PMPid ! {setup_failed, Key, NewKey,
742		     {'EXCEPTION', #'INTERNAL'{completion_status=?COMPLETED_NO}}},
743	    ok;
744	{ok, undefined} ->
745	    orber:dbg("[~p] orber_iiop_pm:handle_call(connect ~p ~p);~n"
746		      "Probably no listener on the given Node/Port or timedout.",
747		      [?LINE, Host, Port], ?DEBUG_LEVEL),
748	    PMPid ! {setup_failed, Key, NewKey,
749		     {'EXCEPTION', #'COMM_FAILURE'{minor=(?ORBER_VMCID bor 1),
750						   completion_status=?COMPLETED_NO}}},
751	    ok;
752	{ok, Child} ->
753	    case init_interceptors(Host, Port, get_socket_data(Key)) of
754		{'EXCEPTION', E} ->
755		    PMPid ! {setup_failed, Key, NewKey, {'EXCEPTION', E}},
756		    ok;
757		Interceptors ->
758		    BiDirCtx = orber:bidir_context(),
759		    Ctx = case orber:exclude_codeset_ctx() of
760			      true ->
761				  BiDirCtx;
762			      _ ->
763				  CodeSetCtx =
764				      #'CONV_FRAME_CodeSetContext'
765				    {char_data =  Chars,
766				     wchar_data = Wchars},
767				  [#'IOP_ServiceContext'
768				   {context_id=?IOP_CodeSets,
769				    context_data = CodeSetCtx} | BiDirCtx]
770			  end,
771		    PMPid ! {setup_successfull, Key, NewKey,
772			     {Child, Ctx, Interceptors}},
773		    ok
774	    end
775    end.
776
777access_allowed(Host, Port, Type, {_,_,UserInterface}) ->
778    Flags = orber:get_flags(),
779    case ?ORB_FLAG_TEST(Flags, ?ORB_ENV_USE_ACL_OUTGOING) of
780	false when UserInterface == 0 ->
781	    get_local_interface(Type);
782	false ->
783	    inet:getaddr(UserInterface, get_ip_family(UserInterface));
784	true ->
785	    SearchFor =
786		case Type of
787		    normal ->
788			tcp_out;
789		    ssl ->
790			ssl_out
791		end,
792	    {ok, Ip} = inet:getaddr(Host, get_ip_family(Host)),
793	    case orber_acl:match(Ip, SearchFor, true) of
794		{true, [], 0} ->
795		    get_local_interface(Type);
796		{true, [], Port} ->
797		    get_local_interface(Type);
798		{true, [], {Min, Max}} when Port >= Min, Port =< Max ->
799		    get_local_interface(Type);
800		{true, [Interface], 0} ->
801		    {ok, NewIp} = inet:getaddr(Interface, get_ip_family(Interface)),
802		    {ok, NewIp, {Host, Port, 0}};
803		{true, [Interface], Port} ->
804
805		    {ok, NewIp} = inet:getaddr(Interface, get_ip_family(Interface)),
806		    {ok, NewIp, {Host, Port, 0}};
807		{true, [Interface], {Min, Max}} when Port >= Min, Port =< Max ->
808
809		    {ok, NewIp} = inet:getaddr(Interface, get_ip_family(Interface)),
810		    {ok, NewIp, {Host, Port, 0}};
811		_ ->
812		    false
813	    end
814    end.
815
816get_local_interface(normal) ->
817    case orber_env:ip_address_local() of
818	[] ->
819	    ok;
820	[Interface] ->
821	    inet:getaddr(Interface, get_ip_family(Interface))
822    end;
823get_local_interface(ssl) ->
824    case orber_env:iiop_ssl_ip_address_local() of
825	[] ->
826	    ok;
827	[Interface] ->
828	    inet:getaddr(Interface, get_ip_family(Interface))
829    end.
830
831get_ip_family(Addr) ->
832    [Family] = orber_socket:get_ip_family_opts(Addr),
833    Family.
834
835invoke_connection_closed(false) ->
836    ok;
837invoke_connection_closed({native, Ref, PIs}) ->
838    (catch orber_pi:closed_out_connection(PIs, Ref));
839invoke_connection_closed({_Type, _PIs}) ->
840    ok.
841
842
843init_interceptors(Host, Port, {SHost, SPort}) ->
844    case orber:get_interceptors() of
845	{native, PIs} ->
846	    case catch orber_pi:new_out_connection(PIs, Host, Port, SHost, SPort) of
847		{'EXIT', R} ->
848		    orber:dbg("[~p] orber_iiop_pm:init_interceptors(~p); Got Exit: ~p.~n"
849			      "One or more Interceptor incorrect or undefined?",
850			      [?LINE, PIs, R], ?DEBUG_LEVEL),
851		    {'EXCEPTION', #'COMM_FAILURE'{minor=(?ORBER_VMCID bor 2),
852						  completion_status=?COMPLETED_NO}};
853		IntRef ->
854		    {native, IntRef, PIs}
855	    end;
856	Other ->
857            %% Either 'false' or {Type, PIs}.
858	    Other
859    end.
860
861
862check_old_ssl_client_options(Options) ->
863    try
864	0 = orber_tb:keysearch(ssl_client_verify, Options,
865			       orber_env:ssl_client_verify()),
866    	1 = orber_tb:keysearch(ssl_client_depth, Options,
867			       orber_env:ssl_client_depth()),
868     	[] = orber_tb:keysearch(ssl_client_certfile, Options,
869				orber_env:ssl_client_certfile()),
870	[] = orber_tb:keysearch(ssl_client_cacertfile, Options,
871				orber_env:ssl_client_cacertfile()),
872	[] = orber_tb:keysearch(ssl_client_password, Options,
873				orber_env:ssl_client_password()),
874	[] = orber_tb:keysearch(ssl_client_keyfile, Options,
875				orber_env:ssl_client_keyfile()),
876	[] = orber_tb:keysearch(ssl_client_ciphers, Options,
877				orber_env:ssl_client_ciphers()),
878	infinity = orber_tb:keysearch(ssl_client_cachetimeout, Options,
879				      orber_env:ssl_client_cachetimeout()),
880	false = orber_tb:keysearch(iiop_ssl_out_keepalive, Options,
881				   orber_env:iiop_ssl_out_keepalive())
882
883    catch
884	_:_ ->
885	    error_logger:warning_report([{application, orber},
886			 "Ignoring deprecated ssl client options used together with the ssl_client_options"])
887    end.
888
889
890
891
892%%-----------------------------------------------------------------
893%% END OF MODULE
894%%-----------------------------------------------------------------
895