1%%-------------------------------------------------------------------- 2%% 3%% %CopyrightBegin% 4%% 5%% Copyright Ericsson AB 1997-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_net.erl 24%% 25%% Description: 26%% This file contains the IIOP communication server 27%% 28%%----------------------------------------------------------------- 29-module(orber_iiop_net). 30 31-behaviour(gen_server). 32 33-include_lib("orber/src/orber_iiop.hrl"). 34 35%%----------------------------------------------------------------- 36%% External exports 37%%----------------------------------------------------------------- 38-export([start/1, connect/5, connections/0, 39 sockname2peername/2, peername2sockname/2, 40 add_connection/5, 41 add/3, remove/1, reconfigure/1, reconfigure/2]). 42 43%%----------------------------------------------------------------- 44%% Internal exports 45%%----------------------------------------------------------------- 46-export([init/1, terminate/2, handle_call/3, 47 handle_cast/2, handle_info/2, code_change/3]). 48 49%%----------------------------------------------------------------- 50%% Server state record and definitions 51%%----------------------------------------------------------------- 52-define(CONNECTION_DB, orber_iiop_net_db). 53 54-record(state, {ports=[], max_connections, db, counter = 1, queue}). 55 56-record(connection, {pid, socket, type, peerdata, localdata, ref = 0}). 57 58-record(listen, {pid, socket, port, type, ref = 0, options, proxy_options = []}). 59 60%%----------------------------------------------------------------- 61%% External interface functions 62%%----------------------------------------------------------------- 63%%----------------------------------------------------------------- 64%% Func: start/1 65%%----------------------------------------------------------------- 66start(Opts) -> 67 gen_server:start_link({local, orber_iiop_net}, orber_iiop_net, Opts, []). 68 69add(IP, normal, Options) -> 70 Port = orber_tb:keysearch(iiop_port, Options, orber_env:iiop_port()), 71 gen_server:call(orber_iiop_net, {add, IP, normal, Port, Options}, infinity); 72add(IP, ssl, Options) -> 73 Port = orber_tb:keysearch(iiop_ssl_port, Options, orber_env:iiop_ssl_port()), 74 gen_server:call(orber_iiop_net, {add, IP, ssl, Port, Options}, infinity). 75 76remove(Ref) -> 77 gen_server:call(orber_iiop_net, {remove, Ref}, infinity). 78 79reconfigure(Options) -> 80 lists:foreach(fun(P) -> 81 P ! {reconfigure, Options} 82 end, 83 do_select([{#connection{pid = '$1', _='_'}, 84 [], ['$1']}])). 85 86reconfigure(Options, Ref) -> 87 case do_select([{#connection{ref = Ref, pid = '$1', _='_'}, 88 [], ['$1']}]) of 89 [Pid] when is_pid(Pid) -> 90 Pid ! {reconfigure, Options}, 91 ok; 92 _ -> 93 {error, "No proxy matched the supplied reference"} 94 end. 95 96connect(Type, S, AcceptPid, Ref, ProxyOptions) -> 97 gen_server:call(orber_iiop_net, {connect, Type, S, AcceptPid, 98 Ref, ProxyOptions}, infinity). 99 100connections() -> 101 do_select([{#connection{peerdata = '$1', _='_'}, [], ['$1']}]). 102 103sockname2peername(SockHost, SockPort) -> 104 do_select([{#connection{peerdata = '$1', 105 localdata = {match_type(SockHost), 106 match_type(SockPort)}, 107 _='_'}, [], ['$1']}]). 108 109 110peername2sockname(PeerHost, PeerPort) -> 111 do_select([{#connection{peerdata = {match_type(PeerHost), 112 match_type(PeerPort)}, 113 localdata = '$1', 114 _='_'}, [], ['$1']}]). 115 116do_select(Pattern) -> 117 case catch ets:select(?CONNECTION_DB, Pattern) of 118 {'EXIT', _What} -> 119 []; 120 Result -> 121 Result 122 end. 123 124match_type(0) -> 125 %% Wildcard port number 126 '_'; 127match_type("") -> 128 %% Wildcard host 129 '_'; 130match_type(Key) -> 131 %% Wildcard not used. 132 Key. 133 134add_connection(Socket, Type, PeerData, LocalData, Ref) -> 135 ets:insert(?CONNECTION_DB, #connection{pid = self(), socket = Socket, 136 type = Type, peerdata = PeerData, 137 localdata = LocalData, ref = Ref}). 138 139%%----------------------------------------------------------------- 140%% Server functions 141%%----------------------------------------------------------------- 142%%----------------------------------------------------------------- 143%% Func: init/1 144%%----------------------------------------------------------------- 145init(Options) -> 146 process_flag(trap_exit, true), 147 {ok, parse_options(Options, 148 #state{max_connections = orber:iiop_max_in_connections(), 149 db = ets:new(?CONNECTION_DB, [set, public, 150 named_table, 151 {keypos, 2}]), 152 queue = queue:new()})}. 153 154%%----------------------------------------------------------------- 155%% Func: terminate/1 156%%----------------------------------------------------------------- 157terminate(_Reason, _State) -> 158 ok. 159 160%%----------------------------------------------------------------- 161%% Func: get_options/2 162%%----------------------------------------------------------------- 163get_options(normal, _Options) -> 164 []; 165get_options(ssl, Options) -> 166 SSLOpts = 167 case orber_tb:keysearch(ssl_server_options, Options, 168 orber_env:ssl_server_options()) of 169 [] -> 170 Verify = orber_tb:keysearch(ssl_server_verify, Options, 171 orber_env:ssl_server_verify()), 172 Depth = orber_tb:keysearch(ssl_server_depth, Options, 173 orber_env:ssl_server_depth()), 174 Cert = orber_tb:keysearch(ssl_server_certfile, Options, 175 orber_env:ssl_server_certfile()), 176 CaCert = orber_tb:keysearch(ssl_server_cacertfile, Options, 177 orber_env:ssl_server_cacertfile()), 178 Pwd = orber_tb:keysearch(ssl_server_password, Options, 179 orber_env:ssl_server_password()), 180 Key = orber_tb:keysearch(ssl_server_keyfile, Options, 181 orber_env:ssl_server_keyfile()), 182 Ciphers = orber_tb:keysearch(ssl_server_ciphers, Options, 183 orber_env:ssl_server_ciphers()), 184 Timeout = orber_tb:keysearch(ssl_server_cachetimeout, Options, 185 orber_env:ssl_server_cachetimeout()), 186 KeepAlive = orber_tb:keysearch(ssl_server_cachetimeout, Options, 187 orber_env:iiop_ssl_in_keepalive()), 188 [{verify, Verify}, 189 {depth, Depth}, 190 {certfile, Cert}, 191 {cacertfile, CaCert}, 192 {password, Pwd}, 193 {keyfile, Key}, 194 {ciphers, Ciphers}, 195 {cachetimeout, Timeout}, 196 {keepalive, KeepAlive}]; 197 Opts -> 198 case orber_tb:check_illegal_tcp_options(Opts) of 199 ok -> 200 check_old_ssl_server_options(Options), 201 Opts; 202 {error, IllegalOpts} -> 203 error_logger:error_report([{application, orber}, 204 "TCP options not allowed to set on a connection", 205 IllegalOpts]), 206 error("Illegal TCP option") 207 end 208 end, 209 ssl_server_extra_options(SSLOpts, []). 210 211%%----------------------------------------------------------------- 212%% Func: parse_options/2 213%%----------------------------------------------------------------- 214parse_options([{port, Type, Port} | Rest], State) -> 215 Options = get_options(Type, []), 216 Family = orber_env:ip_version(), 217 IPFamilyOptions = 218 case Family of 219 inet -> [inet]; 220 inet6 -> [inet6, {ipv6_v6only, true}] 221 end, 222 Options2 = 223 case orber_env:ip_address_variable_defined() of 224 false -> 225 IPFamilyOptions ++ Options; 226 Host -> 227 {ok, IP} = inet:getaddr(Host, Family), 228 IPFamilyOptions ++ [{ip, IP} |Options] 229 end, 230 231 {ok, Listen, NewPort} = orber_socket:listen(Type, Port, Options2, true), 232 {ok, Pid} = orber_iiop_socketsup:start_accept(Type, Listen, 0), 233 link(Pid), 234 ets:insert(?CONNECTION_DB, #listen{pid = Pid, socket = Listen, 235 port = NewPort, type = Type, 236 options = Options2}), 237 parse_options(Rest, State); 238parse_options([], State) -> 239 State. 240 241ssl_server_extra_options([], Acc) -> 242 Acc; 243ssl_server_extra_options([{_Type, []}|T], Acc) -> 244 ssl_server_extra_options(T, Acc); 245ssl_server_extra_options([{_Type, infinity}|T], Acc) -> 246 ssl_server_extra_options(T, Acc); 247ssl_server_extra_options([{Type, Value}|T], Acc) -> 248 ssl_server_extra_options(T, [{Type, Value}|Acc]). 249 250filter_options([], Acc) -> 251 Acc; 252filter_options([{verify, _}|T], Acc) -> 253 filter_options(T, Acc); 254filter_options([{depth, _}|T], Acc) -> 255 filter_options(T, Acc); 256filter_options([{certfile, _}|T], Acc) -> 257 filter_options(T, Acc); 258filter_options([{cacertfile, _}|T], Acc) -> 259 filter_options(T, Acc); 260filter_options([{password, _}|T], Acc) -> 261 filter_options(T, Acc); 262filter_options([{keyfile, _}|T], Acc) -> 263 filter_options(T, Acc); 264filter_options([{ciphers, _}|T], Acc) -> 265 filter_options(T, Acc); 266filter_options([{cachetimeout, _}|T], Acc) -> 267 filter_options(T, Acc); 268filter_options([H|T], Acc) -> 269 filter_options(T, [H|Acc]). 270 271%%----------------------------------------------------------------- 272%% Func: handle_call/3 273%%----------------------------------------------------------------- 274handle_call({remove, Ref}, _From, State) -> 275 case do_select([{#listen{ref = Ref, pid = '$1', socket = '$2', 276 type = '$3', _='_'}, [], [{{'$1', '$2', '$3'}}]}]) of 277 [{Pid, Listen, Type}|_] when is_pid(Pid) -> 278 unlink(Pid), 279 ets:delete(?CONNECTION_DB, Pid), 280 %% Just close the listen socket. Will cause the accept processs 281 %% to terminate. 282 orber_socket:close(Type, Listen), 283 stop_proxies(do_select([{#connection{ref = Ref, pid = '$1', _='_'}, 284 [], ['$1']}])), 285 {reply, ok, 286 State#state{queue = 287 from_list( 288 lists:keydelete(Pid, 1, 289 queue:to_list(State#state.queue)))}}; 290 _ -> 291 {reply, ok, State} 292 end; 293handle_call({add, IP, Type, Port, AllOptions}, _From, State) -> 294 Family = orber_tb:keysearch(ip_family, AllOptions, orber_env:ip_version()), 295 IPFamilyOptions = 296 case Family of 297 inet -> [inet]; 298 inet6 -> [inet6, {ipv6_v6only, true}] 299 end, 300 case inet:getaddr(IP, Family) of 301 {ok, IPTuple} -> 302 try 303 Options = IPFamilyOptions ++ [{ip, IPTuple} |get_options(Type, AllOptions)], 304 Ref = make_ref(), 305 ProxyOptions = filter_options(AllOptions, []), 306 case orber_socket:listen(Type, Port, Options, false) of 307 {ok, Listen, NewPort} -> 308 {ok, Pid} = orber_iiop_socketsup:start_accept(Type, Listen, Ref, 309 ProxyOptions), 310 link(Pid), 311 ets:insert(?CONNECTION_DB, #listen{pid = Pid, 312 socket = Listen, 313 port = NewPort, 314 type = Type, ref = Ref, 315 options = Options, 316 proxy_options = ProxyOptions}), 317 {reply, {ok, Ref}, State}; 318 Error -> 319 {reply, Error, State} 320 end 321 catch 322 error:Reason -> 323 {reply, {error, Reason}, State} 324 end; 325 Other -> 326 {reply, Other, State} 327 end; 328handle_call({connect, Type, Socket, _AcceptPid, AccepRef, ProxyOptions}, _From, State) 329 when State#state.max_connections == infinity; 330 State#state.max_connections > State#state.counter -> 331 case catch access_allowed(Type, Socket, Type) of 332 true -> 333 case orber_iiop_insup:start_connection(Type, Socket, 334 AccepRef, ProxyOptions) of 335 {ok, Pid} when is_pid(Pid) -> 336 link(Pid), 337 {reply, {ok, Pid, true}, update_counter(State, 1)}; 338 Other -> 339 {reply, Other, State} 340 end; 341 _ -> 342 {H, P} = orber_socket:peerdata(Type, Socket), 343 orber_tb:info("Blocked connect attempt from ~s - ~p", [H, P]), 344 {reply, denied, State} 345 end; 346handle_call({connect, Type, Socket, AcceptPid, AccepRef, ProxyOptions}, _From, 347 #state{queue = Q} = State) -> 348 case catch access_allowed(Type, Socket, Type) of 349 true -> 350 case orber_iiop_insup:start_connection(Type, Socket, 351 AccepRef, ProxyOptions) of 352 {ok, Pid} when is_pid(Pid) -> 353 link(Pid), 354 Ref = erlang:make_ref(), 355 {reply, {ok, Pid, Ref}, 356 update_counter(State#state{queue = 357 queue:in({AcceptPid, Ref}, Q)}, 1)}; 358 Other -> 359 {reply, Other, State} 360 end; 361 _ -> 362 {H, P} = orber_socket:peerdata(Type, Socket), 363 orber_tb:info("Blocked connect attempt from ~s - ~p", [H, P]), 364 {reply, denied, State} 365 end; 366handle_call(_, _, State) -> 367 {noreply, State}. 368 369stop_proxies([H|T]) -> 370 catch orber_iiop_inproxy:stop(H), 371 stop_proxies(T); 372stop_proxies([]) -> 373 ok. 374 375access_allowed(Type, Socket, Type) -> 376 Flags = orber:get_flags(), 377 case ?ORB_FLAG_TEST(Flags, ?ORB_ENV_USE_ACL_INCOMING) of 378 false -> 379 true; 380 true -> 381 SearchFor = 382 case Type of 383 normal -> 384 tcp_in; 385 ssl -> 386 ssl_in 387 end, 388 {ok, {Host, Port}} = orber_socket:peername(Type, Socket), 389 case orber_acl:match(Host, SearchFor, true) of 390 {true, [], 0} -> 391 true; 392 {true, [], Port} -> 393 true; 394 {true, [], {Min, Max}} when Port >= Min, Port =< Max -> 395 true; 396 {true, Interfaces, 0} -> 397 get_sockethost(Type, Socket), 398 lists:member(get_sockethost(Type, Socket), Interfaces); 399 {true, Interfaces, Port} -> 400 lists:member(get_sockethost(Type, Socket), Interfaces); 401 {true, Interfaces, {Min, Max}} when Port >= Min, Port =< Max -> 402 lists:member(get_sockethost(Type, Socket), Interfaces); 403 _ -> 404 false 405 end 406 end. 407 408get_sockethost(Type, Socket) -> 409 case orber_socket:peername(Type, Socket) of 410 {ok, {Addr, _Port}} -> 411 orber_env:addr2str(Addr); 412 _ -> 413 false 414 end. 415 416%%------------------------------------------------------------ 417%% Standard gen_server cast handle 418%%------------------------------------------------------------ 419handle_cast(_, State) -> 420 {noreply, State}. 421 422%%------------------------------------------------------------ 423%% Standard gen_server handles 424%%------------------------------------------------------------ 425handle_info({'EXIT', Pid, _Reason}, State) when is_pid(Pid) -> 426 case ets:lookup(?CONNECTION_DB, Pid) of 427 [#listen{pid = Pid, socket = Listen, port = Port, type = Type, 428 ref = Ref, options = Options, proxy_options = POpts}] -> 429 ets:delete(?CONNECTION_DB, Pid), 430 unlink(Pid), 431 {ok, NewPid} = orber_iiop_socketsup:start_accept(Type, Listen, 432 Ref, POpts), 433 link(NewPid), 434 ets:insert(?CONNECTION_DB, #listen{pid = NewPid, socket = Listen, 435 port = Port, type = Type, 436 ref = Ref, options = Options, 437 proxy_options = POpts}), 438 %% Remove the connection if it's in the queue. 439 {noreply, 440 State#state{queue = 441 from_list( 442 lists:keydelete(Pid, 1, 443 queue:to_list(State#state.queue)))}}; 444 [#connection{pid = Pid}] -> 445 ets:delete(?CONNECTION_DB, Pid), 446 unlink(Pid), 447 case queue:out(State#state.queue) of 448 {empty, _} -> 449 {noreply, update_counter(State, -1)}; 450 {{value, {AcceptPid, Ref}}, Q} -> 451 AcceptPid ! {Ref, ok}, 452 {noreply, update_counter(State#state{queue = Q}, -1)} 453 end; 454 [] -> 455 {noreply, State} 456 end; 457handle_info(_, State) -> 458 {noreply, State}. 459 460from_list(List) -> 461 from_list(List, queue:new()). 462 463from_list([], Q) -> 464 Q; 465from_list([H|T], Q) -> 466 NewQ = queue:in(H, Q), 467 from_list(T, NewQ). 468 469 470%%----------------------------------------------------------------- 471%% Func: code_change/3 472%%----------------------------------------------------------------- 473code_change(_OldVsn, State, _Extra) -> 474 {ok, State}. 475 476%%----------------------------------------------------------------- 477%% Internal Functions 478%%----------------------------------------------------------------- 479update_counter(#state{max_connections = infinity} = State, _) -> 480 State; 481update_counter(State, Value) -> 482 State#state{counter = State#state.counter + Value}. 483 484 485check_old_ssl_server_options(Options) -> 486 try 487 0 = orber_tb:keysearch(ssl_server_verify, Options, 488 orber_env:ssl_server_verify()), 489 1 = orber_tb:keysearch(ssl_server_depth, Options, 490 orber_env:ssl_server_depth()), 491 [] = orber_tb:keysearch(ssl_server_certfile, Options, 492 orber_env:ssl_server_certfile()), 493 [] = orber_tb:keysearch(ssl_server_cacertfile, Options, 494 orber_env:ssl_server_cacertfile()), 495 [] = orber_tb:keysearch(ssl_server_password, Options, 496 orber_env:ssl_server_password()), 497 [] = orber_tb:keysearch(ssl_server_keyfile, Options, 498 orber_env:ssl_server_keyfile()), 499 [] = orber_tb:keysearch(ssl_server_ciphers, Options, 500 orber_env:ssl_server_ciphers()), 501 infinity = orber_tb:keysearch(ssl_server_cachetimeout, Options, 502 orber_env:ssl_server_cachetimeout()), 503 false = orber_tb:keysearch(iiop_ssl_in_keepalive, Options, 504 orber_env:iiop_ssl_in_keepalive()) 505 catch 506 _:_ -> 507 io:format("hej\n",[]), 508 error_logger:warning_report([{application, orber}, 509 "Ignoring deprecated ssl server options used together with the ssl_server_options"]) 510 end. 511 512