aboutsummaryrefslogtreecommitdiffstats
path: root/lib/orber/src/orber_socket.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/orber/src/orber_socket.erl')
-rw-r--r--lib/orber/src/orber_socket.erl504
1 files changed, 504 insertions, 0 deletions
diff --git a/lib/orber/src/orber_socket.erl b/lib/orber/src/orber_socket.erl
new file mode 100644
index 0000000000..2a64bd4e75
--- /dev/null
+++ b/lib/orber/src/orber_socket.erl
@@ -0,0 +1,504 @@
+%%--------------------------------------------------------------------
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. 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_socket.erl
+%%
+%% Description:
+%% This file contains a standard interface to the sockets to handle the differences
+%% between the implementations used.
+%%
+%%-----------------------------------------------------------------
+-module(orber_socket).
+
+-include_lib("orber/include/corba.hrl").
+-include_lib("orber/src/orber_iiop.hrl").
+
+
+%%-----------------------------------------------------------------
+%% External exports
+%%-----------------------------------------------------------------
+-export([start/0, connect/4, listen/3, listen/4, accept/2, accept/3, write/3,
+ controlling_process/3, close/2, peername/2, sockname/2,
+ peerdata/2, peercert/2, peercert/3, sockdata/2, setopts/3,
+ clear/2, shutdown/3, post_accept/2, post_accept/3]).
+
+%%-----------------------------------------------------------------
+%% Internal exports
+%%-----------------------------------------------------------------
+-export([]).
+
+%%-----------------------------------------------------------------
+%% Internal defines
+%%-----------------------------------------------------------------
+-define(DEBUG_LEVEL, 6).
+
+%%-----------------------------------------------------------------
+%% External functions
+%%-----------------------------------------------------------------
+start() ->
+ inet_db:start().
+
+%%-----------------------------------------------------------------
+%% Invoke the required setopts (i.e., inet or ssl)
+setopts(normal, Socket, Opts) ->
+ inet:setopts(Socket, Opts);
+setopts(ssl, Socket, Opts) ->
+ ssl:setopts(Socket, Opts).
+
+%%-----------------------------------------------------------------
+%% Connect to IIOP Port at Host in CDR mode, in order to
+%% establish a connection.
+%%
+connect(Type, Host, Port, Options) ->
+ Timeout = orber:iiop_setup_connection_timeout(),
+ Generation = orber_env:ssl_generation(),
+ Options1 = check_options(Type, Options, Generation),
+ Options2 =
+ case Type of
+ normal ->
+ [{keepalive, orber_env:iiop_out_keepalive()}|Options1];
+ _ when Generation > 2 ->
+ [{keepalive, orber_env:iiop_ssl_out_keepalive()}|Options1];
+ _ ->
+ Options1
+ end,
+ case orber:iiop_out_ports() of
+ {Min, Max} when Type == normal ->
+ multi_connect(Min, Max, Type, Host, Port,
+ [binary, {reuseaddr, true},
+ {packet,cdr}| Options2], Timeout);
+ {Min, Max} when Generation > 2 ->
+ multi_connect(Min, Max, Type, Host, Port,
+ [binary, {reuseaddr, true},
+ {packet,cdr}| Options2], Timeout);
+ {Min, Max} ->
+ %% reuseaddr not available for older SSL versions
+ multi_connect(Min, Max, Type, Host, Port,
+ [binary, {packet,cdr}| Options2], Timeout);
+ _ ->
+ connect(Type, Host, Port, [binary, {packet,cdr}| Options2], Timeout)
+ end.
+
+connect(normal, Host, Port, Options, Timeout) ->
+ case catch gen_tcp:connect(Host, Port, Options, Timeout) of
+ {ok, Socket} ->
+ Socket;
+ {error, timeout} ->
+ orber:dbg("[~p] orber_socket:connect(normal, ~p, ~p, ~p);~n"
+ "Timeout after ~p msec.",
+ [?LINE, Host, Port, Options, Timeout], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{minor=(?ORBER_VMCID bor 4),
+ completion_status=?COMPLETED_NO});
+ Error ->
+ orber:dbg("[~p] orber_socket:connect(normal, ~p, ~p, ~p);~n"
+ "Failed with reason: ~p",
+ [?LINE, Host, Port, Options, Error], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end;
+connect(ssl, Host, Port, Options, Timeout) ->
+ case catch ssl:connect(Host, Port, Options, Timeout) of
+ {ok, Socket} ->
+ Socket;
+ {error, timeout} ->
+ orber:dbg("[~p] orber_socket:connect(ssl, ~p, ~p, ~p);~n"
+ "Timeout after ~p msec.",
+ [?LINE, Host, Port, Options, Timeout], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{minor=(?ORBER_VMCID bor 4),
+ completion_status=?COMPLETED_NO});
+ Error ->
+ orber:dbg("[~p] orber_socket:connect(ssl, ~p, ~p, ~p);~n"
+ "Failed with reason: ~p",
+ [?LINE, Host, Port, Options, Error], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end.
+
+multi_connect(CurrentPort, Max, Type, Host, Port, Options, _) when CurrentPort > Max ->
+ orber:dbg("[~p] orber_socket:multi_connect(~p, ~p, ~p, ~p);~n"
+ "Unable to use any of the sockets defined by 'iiop_out_ports'.~n"
+ "Either all ports are in use or to many connections already exists.",
+ [?LINE, Type, Host, Port, Options], ?DEBUG_LEVEL),
+ corba:raise(#'IMP_LIMIT'{minor=(?ORBER_VMCID bor 1), completion_status=?COMPLETED_NO});
+multi_connect(CurrentPort, Max, normal, Host, Port, Options, Timeout) ->
+ case catch gen_tcp:connect(Host, Port, [{port, CurrentPort}|Options], Timeout) of
+ {ok, Socket} ->
+ Socket;
+ {error, timeout} ->
+ orber:dbg("[~p] orber_socket:multi_connect(normal, ~p, ~p, ~p);~n"
+ "Timeout after ~p msec.",
+ [?LINE, Host, Port, [{port, CurrentPort}|Options],
+ Timeout], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{minor=(?ORBER_VMCID bor 4),
+ completion_status=?COMPLETED_NO});
+ _ ->
+ multi_connect(CurrentPort+1, Max, normal, Host, Port, Options, Timeout)
+ end;
+multi_connect(CurrentPort, Max, ssl, Host, Port, Options, Timeout) ->
+ case catch ssl:connect(Host, Port, [{port, CurrentPort}|Options], Timeout) of
+ {ok, Socket} ->
+ Socket;
+ {error, timeout} ->
+ orber:dbg("[~p] orber_socket:multi_connect(ssl, ~p, ~p, ~p);~n"
+ "Timeout after ~p msec.",
+ [?LINE, Host, Port, [{port, CurrentPort}|Options],
+ Timeout], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{minor=(?ORBER_VMCID bor 4),
+ completion_status=?COMPLETED_NO});
+ _ ->
+ multi_connect(CurrentPort+1, Max, ssl, Host, Port, Options, Timeout)
+ end.
+
+
+
+%%-----------------------------------------------------------------
+%% Create a listen socket at Port in CDR mode for
+%% data connection.
+%%
+listen(Type, Port, Options) ->
+ listen(Type, Port, Options, true).
+
+listen(normal, Port, Options, Exception) ->
+ Options1 = check_options(normal, Options, 0),
+ Backlog = orber:iiop_backlog(),
+ Keepalive = orber_env:iiop_in_keepalive(),
+ Options2 = case orber:iiop_max_in_requests() of
+ infinity ->
+ Options1;
+ _MaxRequests ->
+ [{active, once}|Options1]
+ end,
+ Options3 = case orber_env:iiop_packet_size() of
+ infinity ->
+ Options2;
+ MaxSize ->
+ [{packet_size, MaxSize}|Options2]
+ end,
+ case catch gen_tcp:listen(Port, [binary, {packet,cdr}, {keepalive, Keepalive},
+ {reuseaddr,true}, {backlog, Backlog} |
+ Options3]) of
+ {ok, ListenSocket} ->
+ {ok, ListenSocket, check_port(Port, normal, ListenSocket)};
+ {error, Reason} when Exception == false ->
+ {error, Reason};
+ {error, eaddrinuse} ->
+ AllOpts = [binary, {packet,cdr},
+ {reuseaddr,true} | Options3],
+ orber:dbg("[~p] orber_socket:listen(normal, ~p, ~p);~n"
+ "Looks like the listen port is already in use.~n"
+ "Check if another Orber is started~n"
+ "on the same node and uses the same listen port (iiop_port). But it may also~n"
+ "be used by any other application; confirm with 'netstat'.",
+ [?LINE, Port, AllOpts], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO});
+ Error ->
+ AllOpts = [binary, {packet,cdr},
+ {reuseaddr,true} | Options3],
+ orber:dbg("[~p] orber_socket:listen(normal, ~p, ~p);~n"
+ "Failed with reason: ~p",
+ [?LINE, Port, AllOpts, Error], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end;
+listen(ssl, Port, Options, Exception) ->
+ Backlog = orber:iiop_ssl_backlog(),
+ Generation = orber_env:ssl_generation(),
+ Options1 = check_options(ssl, Options, Generation),
+ Options2 = case orber:iiop_max_in_requests() of
+ infinity ->
+ Options1;
+ _MaxRequests ->
+ [{active, once}|Options1]
+ end,
+ Options3 = case orber_env:iiop_packet_size() of
+ infinity ->
+ Options2;
+ MaxSize ->
+ [{packet_size, MaxSize}|Options2]
+ end,
+ Options4 = if
+ Generation > 2 ->
+ [{reuseaddr, true},
+ {keepalive, orber_env:iiop_ssl_in_keepalive()}|Options3];
+ true ->
+ Options3
+ end,
+ case catch ssl:listen(Port, [binary, {packet,cdr},
+ {backlog, Backlog} | Options4]) of
+ {ok, ListenSocket} ->
+ {ok, ListenSocket, check_port(Port, ssl, ListenSocket)};
+ {error, Reason} when Exception == false ->
+ {error, Reason};
+ {error, eaddrinuse} ->
+ AllOpts = [binary, {packet,cdr} | Options4],
+ orber:dbg("[~p] orber_socket:listen(ssl, ~p, ~p);~n"
+ "Looks like the listen port is already in use. Check if~n"
+ "another Orber is started on the same node and uses the~n"
+ "same listen port (iiop_port). But it may also~n"
+ "be used by any other application; confirm with 'netstat'.",
+ [?LINE, Port, AllOpts], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO});
+ Error ->
+ AllOpts = [binary, {packet,cdr} | Options4],
+ orber:dbg("[~p] orber_socket:listen(ssl, ~p, ~p);~n"
+ "Failed with reason: ~p",
+ [?LINE, Port, AllOpts, Error], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end.
+
+%%-----------------------------------------------------------------
+%% Wait in accept on the socket
+%%
+accept(Type, ListenSocket) ->
+ accept(Type, ListenSocket, infinity).
+
+accept(normal, ListenSocket, _Timeout) ->
+ case catch gen_tcp:accept(ListenSocket) of
+ {ok, S} ->
+ S;
+ Error ->
+ orber:dbg("[~p] orber_socket:accept(normal, ~p);~n"
+ "Failed with reason: ~p",
+ [?LINE, ListenSocket, Error], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end;
+accept(ssl, ListenSocket, Timeout) ->
+ case catch ssl:transport_accept(ListenSocket, Timeout) of
+ {ok, S} ->
+ S;
+ Error ->
+ orber:dbg("[~p] orber_socket:accept(ssl, ~p);~n"
+ "Failed with reason: ~p",
+ [?LINE, ListenSocket, Error], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end.
+
+post_accept(Type, Socket) ->
+ post_accept(Type, Socket, infinity).
+
+post_accept(normal, _Socket, _Timeout) ->
+ ok;
+post_accept(ssl, Socket, Timeout) ->
+ case catch ssl:ssl_accept(Socket, Timeout) of
+ ok ->
+ ok;
+ Error ->
+ orber:dbg("[~p] orber_socket:post_accept(ssl, ~p);~n"
+ "Failed with reason: ~p",
+ [?LINE, Socket, Error], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end.
+
+
+%%-----------------------------------------------------------------
+%% Close the socket
+%%
+close(normal, Socket) ->
+ (catch gen_tcp:close(Socket));
+close(ssl, Socket) ->
+ (catch ssl:close(Socket)).
+
+%%-----------------------------------------------------------------
+%% Write to socket
+%%
+write(normal, Socket, Bytes) ->
+ gen_tcp:send(Socket, Bytes);
+write(ssl, Socket, Bytes) ->
+ ssl:send(Socket, Bytes).
+
+%%-----------------------------------------------------------------
+%% Change the controlling process for the socket
+%%
+controlling_process(normal, Socket, Pid) ->
+ gen_tcp:controlling_process(Socket, Pid);
+controlling_process(ssl, Socket, Pid) ->
+ ssl:controlling_process(Socket, Pid).
+
+%%-----------------------------------------------------------------
+%% Get peername
+%%
+peername(normal, Socket) ->
+ inet:peername(Socket);
+peername(ssl, Socket) ->
+ ssl:peername(Socket).
+
+%%-----------------------------------------------------------------
+%% Get peercert
+%%
+peercert(ssl, Socket) ->
+ ssl:peercert(Socket);
+peercert(Type, _Socket) ->
+ orber:dbg("[~p] orber_socket:peercert(~p);~n"
+ "Only available for SSL sockets.",
+ [?LINE, Type], ?DEBUG_LEVEL),
+ {error, ebadsocket}.
+
+peercert(ssl, Socket, Opts) ->
+ ssl:peercert(Socket, Opts);
+peercert(Type, _Socket, Opts) ->
+ orber:dbg("[~p] orber_socket:peercert(~p, ~p);~n"
+ "Only available for SSL sockets.",
+ [?LINE, Type, Opts], ?DEBUG_LEVEL),
+ {error, ebadsocket}.
+
+%%-----------------------------------------------------------------
+%% Get peerdata
+%%
+peerdata(normal, Socket) ->
+ create_data(inet:peername(Socket));
+peerdata(ssl, Socket) ->
+ create_data(ssl:peername(Socket)).
+
+%%-----------------------------------------------------------------
+%% Get sockname
+%%
+sockname(normal, Socket) ->
+ inet:sockname(Socket);
+sockname(ssl, Socket) ->
+ ssl:sockname(Socket).
+
+%%-----------------------------------------------------------------
+%% Get sockdata
+%%
+sockdata(normal, Socket) ->
+ create_data(inet:sockname(Socket));
+sockdata(ssl, Socket) ->
+ create_data(ssl:sockname(Socket)).
+
+
+create_data({ok, {Addr, Port}}) ->
+ {orber_env:addr2str(Addr), Port};
+create_data(What) ->
+ orber:dbg("[~p] orber_socket:peername() or orber_socket:sockname();~n"
+ "Failed with reason: ~p", [?LINE, What], ?DEBUG_LEVEL),
+ {"Unable to lookup peer- or sockname", 0}.
+
+
+%%-----------------------------------------------------------------
+%% Shutdown Connection
+%% How = read | write | read_write
+shutdown(normal, Socket, How) ->
+ gen_tcp:shutdown(Socket, How);
+shutdown(ssl, Socket, How) ->
+ Generation = orber_env:ssl_generation(),
+ if
+ Generation > 2 ->
+ ssl:shutdown(Socket, How);
+ How == read_write ->
+ %% Older versions of SSL do no support shutdown.
+ %% For now we'll use this solution instead.
+ close(ssl, Socket);
+ true ->
+ {error, undefined}
+ end.
+
+%%-----------------------------------------------------------------
+%% Remove Messages from queue
+%%
+clear(normal, Socket) ->
+ tcp_clear(Socket);
+clear(ssl, Socket) ->
+ ssl_clear(Socket).
+
+
+
+%% Inet also checks for the following messages:
+%% * {S, {data, Data}}
+%% * {inet_async, S, Ref, Status},
+%% * {inet_reply, S, Status}
+%% SSL doesn't.
+tcp_clear(Socket) ->
+ receive
+ {tcp, Socket, _Data} ->
+ tcp_clear(Socket);
+ {tcp_closed, Socket} ->
+ tcp_clear(Socket);
+ {tcp_error, Socket, _Reason} ->
+ tcp_clear(Socket)
+ after 0 ->
+ ok
+ end.
+
+ssl_clear(Socket) ->
+ receive
+ {ssl, Socket, _Data} ->
+ ssl_clear(Socket);
+ {ssl_closed, Socket} ->
+ ssl_clear(Socket);
+ {ssl_error, Socket, _Reason} ->
+ ssl_clear(Socket)
+ after 0 ->
+ ok
+ end.
+
+
+
+%%-----------------------------------------------------------------
+%% Check Port. If the user supplies 0 we pick any vacant port. But then
+%% we must change the associated environment variable
+check_port(0, normal, Socket) ->
+ case inet:port(Socket) of
+ {ok, Port} ->
+ orber:configure_override(iiop_port, Port),
+ Port;
+ What ->
+ orber:dbg("[~p] orber_socket:check_port(~p);~n"
+ "Unable to extract the port number via inet:port/1~n",
+ [?LINE, What], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end;
+check_port(0, ssl, Socket) ->
+ case ssl:sockname(Socket) of
+ {ok, {_Address, Port}} ->
+ orber:configure_override(iiop_ssl_port, Port),
+ Port;
+ What ->
+ orber:dbg("[~p] orber_socket:check_port(~p);~n"
+ "Unable to extract the port number via ssl:sockname/1~n",
+ [?LINE, What], ?DEBUG_LEVEL),
+ corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO})
+ end;
+check_port(Port, _, _) ->
+ Port.
+
+%%-----------------------------------------------------------------
+%% Check Options.
+%% We need this as a work-around since the SSL-app doesn't allow us
+%% to pass 'inet' as an option. Also needed for R9B :-(
+check_options(normal, Options, _Generation) ->
+ case orber:ip_version() of
+ inet ->
+ Options;
+ inet6 ->
+ %% Necessary for R9B. Should be [orber:ip_version()|Options];
+ [inet6|Options]
+ end;
+check_options(ssl, Options, Generation) ->
+ case orber:ip_version() of
+ inet when Generation > 2 ->
+ [{ssl_imp, new}|Options];
+ inet ->
+ Options;
+ inet6 when Generation > 2 ->
+ [{ssl_imp, new}, inet6|Options];
+ inet6 ->
+ %% Will fail until SSL supports this option.
+ %% Note, we want this happen!
+ [inet6|Options]
+ end.
+