%%-------------------------------------------------------------------- %% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1997-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% %%----------------------------------------------------------------- %% File: orber_iiop.erl %% Description: %% This file contains the interface to the iiop operations %% %%----------------------------------------------------------------- -module(orber_iiop). -include_lib("orber/include/corba.hrl"). -include_lib("orber/src/orber_iiop.hrl"). -behaviour(supervisor). %%----------------------------------------------------------------- %% External exports %%----------------------------------------------------------------- -export([start_sup/1, request/8, locate/4]). %%----------------------------------------------------------------- %% Internal exports %%----------------------------------------------------------------- -export([init/1, terminate/2, handle_call/3]). %%----------------------------------------------------------------- %% Internal defines %%----------------------------------------------------------------- -define(DEBUG_LEVEL, 7). %%----------------------------------------------------------------- %% External interface functions %%----------------------------------------------------------------- %%----------------------------------------------------------------- %% Func: start_sup/1 %%----------------------------------------------------------------- start_sup(Opts) -> supervisor:start_link({local, orber_iiop_sup}, ?MODULE, {orber_iiop_sup, Opts}). %%%----------------------------------------------------------------- %%% Func: connect/1 %%%----------------------------------------------------------------- %connect(OrbName) -> % orber_iiop_net:connect(OrbName). %%%----------------------------------------------------------------- %%% Func: request/5 %%%----------------------------------------------------------------- request({Host, Port, InitObjkey, Index, TaggedProfile, HostData}, Op, Parameters, TypeCodes, ResponseExpected, Timeout, IOR, UserCtx) -> {{Proxy, SysCtx, Interceptors, LocalInterface}, ObjKey, Version} = connect(Host, Port, InitObjkey, Timeout, [Index], HostData, TaggedProfile, IOR, UserCtx), Ctx = add_user_context(SysCtx, UserCtx), RequestId = orber_request_number:get(), Env = #giop_env{interceptors = Interceptors, type = out, flags = orber_env:get_flags(), host = LocalInterface, version = Version, ctx = Ctx, request_id = RequestId, op = Op, parameters = Parameters, tc = TypeCodes, objkey = ObjKey, response_expected = ResponseExpected}, Message = encode_request(Env), case catch orber_iiop_outproxy:request(Proxy, ResponseExpected, Timeout, Message, RequestId) of {'EXCEPTION', MsgExc} -> corba:raise(MsgExc); _ when ResponseExpected == false -> ok; {reply, ReplyHeader, Rest, Len, ByteOrder, Bytes} -> case catch decode_reply_body(Interceptors, ObjKey, Op, ReplyHeader, Version, TypeCodes, Rest, Len, ByteOrder, Bytes) of {'EXCEPTION', DecodeException} -> %% We cannot log this exception since it may be a correct exception. corba:raise(DecodeException); {'EXIT', message_error} -> orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" "Got exit(message_error)", [?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); {'EXIT', Why} -> orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" "Got exit(~p)", [?LINE, Rest, Version, TypeCodes, Why], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); 'message_error' -> orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p);~n" "Got message_error", [?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL), %% Perhaps a resend should be done when a message error occurs corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); {Result, Par} -> %% Check request id case ReplyHeader#reply_header.reply_status of 'no_exception' -> case Par of [] -> Result; _ -> list_to_tuple([Result | Par]) end; 'system_exception' -> corba:raise(Result); 'user_exception' -> corba:raise(Result); 'location_forward' -> case get(orber_forward_notify) of true -> {location_forward, Result}; _ -> case catch corba:call(Result, Op, Parameters, TypeCodes, [{timeout, Timeout}, {context, UserCtx}]) of {'EXCEPTION', E} -> corba:raise(E); {'EXIT', Reason} -> orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" "location_forward resulted in exit(~p)", [?LINE, Rest, Version, TypeCodes, Reason], ?DEBUG_LEVEL), corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}); NewResult -> NewResult end end; 'location_forward_perm' -> %% We should notify the client in this case. case get(orber_forward_notify) of true -> {location_forward, Result}; _ -> case catch corba:call(Result, Op, Parameters, TypeCodes, [{timeout, Timeout}, {context, UserCtx}]) of {'EXCEPTION', E} -> corba:raise(E); {'EXIT', Reason} -> orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" "location_forward_perm resulted in exit(~p)", [?LINE, Rest, Version, TypeCodes, Reason], ?DEBUG_LEVEL), corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}); NewResult -> NewResult end end; 'needs_addressing_mode' -> orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" "needs_addressing_mode not supported.", [?LINE, Rest, Version, TypeCodes], ?DEBUG_LEVEL), corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}) end end; What -> orber:dbg("[~p] orber_iiop:request(reply, ~p, ~p, ~p)~n" "outproxy-request: ~p", [?LINE, Message, Version, TypeCodes, What], ?DEBUG_LEVEL), corba:raise(#'COMM_FAILURE'{completion_status=?COMPLETED_NO}) end. -dialyzer({no_improper_lists, encode_request/1}). encode_request(#giop_env{interceptors = false} = Env) -> case catch cdr_encode:enc_request(Env) of {'EXCEPTION', Exc} -> orber:dbg("[~p] orber_iiop:request(~p)~n" "Got exception(~p)", [?LINE, Env, Exc], ?DEBUG_LEVEL), corba:raise(Exc); {'EXIT', R} -> orber:dbg("[~p] orber_iiop:request:( ~p )~n" "Got exit(~p)", [?LINE, Env, R], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); Msg -> Msg end; encode_request(#giop_env{interceptors = {native, Ref, PIs}, objkey = ObjKey, ctx = Ctx, op = Op, parameters = Params} = Env) -> Parameters = orber_pi:out_request(PIs, ObjKey, Ctx, Op, Ref, Params), case catch cdr_encode:enc_request_split(Env) of {'EXCEPTION', Exc} -> orber:dbg("[~p] orber_iiop:request( ~p, ~p); exception(~p)", [?LINE, Env, Parameters, Exc], ?DEBUG_LEVEL), corba:raise(Exc); {'EXIT', R} -> orber:dbg("[~p] orber_iiop:request:( ~p, ~p); got exit(~p)", [?LINE, Env, Parameters, R], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); {Hdr, Body, HdrLen, _, Flags} -> NewBody = orber_pi:out_request_enc(PIs, ObjKey, Ctx, Op, Ref, Body), cdr_encode:enc_giop_message_header(Env, 'request', Flags, HdrLen+size(NewBody), [Hdr|NewBody]) end; encode_request(Env) -> case catch cdr_encode:enc_request(Env) of {'EXCEPTION', Exc} -> orber:dbg("[~p] orber_iiop:request( ~p ); exception(~p)", [?LINE, Env, Exc], ?DEBUG_LEVEL), corba:raise(Exc); {'EXIT', R} -> orber:dbg("[~p] orber_iiop:request:( ~p ); got exit(~p)", [?LINE, Env, R], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); Msg -> Msg end. %%----------------------------------------------------------------- %% Func: locate/1 %%----------------------------------------------------------------- locate({Host, Port, InitObjkey, Index, TaggedProfile, HostData}, Timeout, IOR, UserCtx) -> {{Proxy, _Ctx, _Interceptors, LocalInterface}, ObjKey, Version} = connect(Host, Port, InitObjkey, Timeout, [Index], HostData, TaggedProfile, IOR, UserCtx), RequestId = orber_request_number:get(), Env = #giop_env{version = Version, objkey = ObjKey, request_id = RequestId, flags = orber_env:get_flags(), host = LocalInterface}, Result = case catch cdr_encode:enc_locate_request(Env) of {'EXCEPTION', EncE} -> orber:dbg("[~p] orber_iiop:locate(~p); exception(~p)", [?LINE, ObjKey, EncE], ?DEBUG_LEVEL), corba:raise(EncE); {'EXIT', EncR} -> orber:dbg("[~p] orber_iiop:locate(~p); exit(~p)", [?LINE, ObjKey, EncR], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}); Request -> (catch orber_iiop_outproxy:request(Proxy, true, Timeout, Request, RequestId)) end, case Result of {'EXCEPTION', MsgExc} -> corba:raise(MsgExc); {locate_reply, ReplyHeader, Rest, Len, ByteOrder} -> case catch cdr_decode:dec_locate_reply_body(Version, ReplyHeader#locate_reply_header.locate_status, Rest, Len, ByteOrder) of {'EXCEPTION', DecodeException} -> orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exception(~p)", [?LINE, Rest, Version, DecodeException], ?DEBUG_LEVEL), corba:raise(DecodeException); {'EXIT', message_error} -> orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exit(message_error)", [?LINE, Rest, Version], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); {'EXIT', R} -> orber:dbg("[~p] orber_iiop:locate(locate_reply, ~p, ~p); exit(~p)", [?LINE, Rest, Version, R], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_MAYBE}); [] -> ReplyHeader#locate_reply_header.locate_status; ObjRef -> {ReplyHeader#locate_reply_header.locate_status, ObjRef} end; Other -> orber:dbg("[~p] orber_iiop:locate(~p); exit(~p)", [?LINE, ObjKey, Other], ?DEBUG_LEVEL), corba:raise(#'MARSHAL'{completion_status=?COMPLETED_NO}) end. %%%----------------------------------------------------------------- %%% Func: cancel/1 %%%----------------------------------------------------------------- %cancel(X) -> % ok. %%%----------------------------------------------------------------- %%% Func: message_error/1 %%%----------------------------------------------------------------- %message_error(X) -> % ok. %%----------------------------------------------------------------- %% Server functions %%----------------------------------------------------------------- %%----------------------------------------------------------------- %% Func: init/1 %%----------------------------------------------------------------- init({orber_iiop_sup, Opts}) -> IIOP_port = orber:iiop_port(), SSL_port = orber:iiop_ssl_port(), SupFlags = {one_for_one, 5, 1000}, %Max 5 restarts in 1 second PortList = if SSL_port > -1 -> [{port, ssl, SSL_port}]; true -> [] end, ChildSpec = case orber:is_lightweight() of true -> [ {orber_iiop_outsup, {orber_iiop_outsup, start, [sup, Opts]}, permanent, 10000, supervisor, [orber_iiop_outsup]}, {orber_iiop_pm, {orber_iiop_pm, start, [Opts]}, permanent, 10000, worker, [orber_iiop_pm]} ]; false -> [{orber_iiop_outsup, {orber_iiop_outsup, start, [sup, Opts]}, permanent, 10000, supervisor, [orber_iiop_outsup]}, {orber_iiop_pm, {orber_iiop_pm, start, [Opts]}, permanent, 10000, worker, [orber_iiop_pm]}, {orber_iiop_insup, {orber_iiop_insup, start, [sup, Opts]}, permanent, 10000, supervisor, [orber_iiop_insup]}, {orber_iiop_socketsup, {orber_iiop_socketsup, start, [sup, Opts]}, permanent, 10000, supervisor, [orber_iiop_socketsup]}, {orber_iiop_net, {orber_iiop_net, start, [[{port, normal, IIOP_port} | PortList]]}, permanent, 10000, worker, [orber_iiop_net]}] end, {ok, {SupFlags, ChildSpec}}. %%----------------------------------------------------------------- %% Func: terminate/2 %%----------------------------------------------------------------- terminate(_Reason, _State) -> ok. %%----------------------------------------------------------------- %% Func: handle_call/3 %%----------------------------------------------------------------- handle_call(_Req, _From, State) -> {reply, ok, State}. %%----------------------------------------------------------------- %% Internal functions %%----------------------------------------------------------------- add_user_context([], UserCtx) -> UserCtx; add_user_context(SysCtx, []) -> SysCtx; add_user_context(SysCtx, UserCtx) -> SysCtx ++ UserCtx. decode_reply_body(false, _ObjKey, _Op, ReplyHeader, Version, TypeCodes, Rest, Len, ByteOrder, Bytes) -> case ReplyHeader#reply_header.reply_status of 'no_exception' -> {R, P, _} = cdr_decode:dec_reply_body(Version, TypeCodes, Rest, Len, ByteOrder, Bytes), {R, P}; 'system_exception' -> {R, _} = cdr_decode:dec_system_exception(Version, Rest, Len, ByteOrder), {R, []}; 'user_exception' -> {R, _} = cdr_decode:dec_user_exception(Version, Rest, Len, ByteOrder), {R, []}; 'location_forward' -> {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, Rest, Len, ByteOrder, Bytes), {R, []}; 'location_forward_perm' -> {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, Rest, Len, ByteOrder, Bytes), {R, []}; 'needs_addressing_mode' -> {R, _, _} = cdr_decode:dec_reply_body(Version, {'tk_short', [],[]}, Rest, Len, ByteOrder, Bytes), {R, []} end; decode_reply_body(Interceptors, ObjKey, Op, ReplyHeader, Version, TypeCodes, RestIn, Len, ByteOrder, Bytes) -> Rest = case Interceptors of {portable, _PIs} -> RestIn; {native, Ref, PIs} -> orber_pi:in_reply_enc(PIs, ObjKey, ReplyHeader#reply_header.service_context, Op, Ref, RestIn) end, Reply = case ReplyHeader#reply_header.reply_status of 'no_exception' -> {R, P, _} = cdr_decode:dec_reply_body(Version, TypeCodes, Rest, Len, ByteOrder, Bytes), {R, P}; 'system_exception' -> {R, _} = cdr_decode:dec_system_exception(Version, Rest, Len, ByteOrder), {R, []}; 'user_exception' -> {R, _} = cdr_decode:dec_user_exception(Version, Rest, Len, ByteOrder), {R, []}; 'location_forward' -> {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, Rest, Len, ByteOrder, Bytes), {R, []}; 'location_forward_perm' -> {R, _, _} = cdr_decode:dec_reply_body(Version, {{'tk_objref', "", ""}, [],[]}, Rest, Len, ByteOrder, Bytes), {R, []}; 'needs_addressing_mode' -> {R, _, _} = cdr_decode:dec_reply_body(Version, {'tk_short', [],[]}, Rest, Len, ByteOrder, Bytes), {R, []} end, case Interceptors of {portable, _PI} -> Reply; {native, Refs, PI} -> orber_pi:in_reply(PI, ObjKey, ReplyHeader#reply_header.service_context, Op, Refs, Reply) end. %% "Plain" TCP/IP. connect(Host, Port, Objkey, Timeout, Index, #host_data{protocol = normal, csiv2_mech = undefined} = HostData, TaggedProfile, IOR, Ctx) -> connect2([{Host, Port}], Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx); %% "Plain" SSL connect(Host, _, Objkey, Timeout, Index, #host_data{protocol = ssl, ssl_data = #'SSLIOP_SSL'{port = Port}, csiv2_mech = undefined} = HostData, TaggedProfile, IOR, Ctx) -> connect2([{Host, Port}], Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx); %% TEMPORARY FIX TO AVOID RUNNING CSIv2. connect(Host, _, Objkey, Timeout, Index, #host_data{protocol = ssl, ssl_data = #'SSLIOP_SSL'{port = Port}} = HostData, TaggedProfile, IOR, Ctx) -> connect2([{Host, Port}], Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx); %% CSIv2 over SSL (TAG_TLS_SEC_TRANS) using the SAS protocol. Note port must equal 0. connect(_Host, 0, Objkey, Timeout, Index, #host_data{protocol = ssl, csiv2_mech = #'CSIIOP_CompoundSecMech'{target_requires = _TR} = _Mech, csiv2_addresses = Addresses} = HostData, TaggedProfile, IOR, Ctx) -> NewCtx = [#'IOP_ServiceContext' {context_id=?IOP_SecurityAttributeService, context_data = #'CSI_SASContextBody' {label = ?CSI_MsgType_MTEstablishContext, value = #'CSI_EstablishContext' {client_context_id = 0, %% Always 0 when stateless. authorization_token = [#'CSI_AuthorizationElement'{the_element = []}], identity_token = #'CSI_IdentityToken'{label = ?CSI_IdentityTokenType_ITTAbsent, value = true}, client_authentication_token = []}}}|Ctx], connect2(Addresses, Objkey, Timeout, Index, HostData, TaggedProfile, IOR, NewCtx); %% CSIv2 over SSL (TAG_NULL_TAG) using the SAS protocol. connect(Host, _, Objkey, Timeout, Index, #host_data{protocol = ssl, ssl_data = #'SSLIOP_SSL'{port = Port}, csiv2_mech = Mech} = HostData, TaggedProfile, IOR, Ctx) when is_record(Mech, 'CSIIOP_CompoundSecMech') -> connect2([{Host, Port}], Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx); %% CSIv2 over TCP (TAG_NULL_TAG) using the SAS protocol. connect(Host, Port, Objkey, Timeout, Index, #host_data{protocol = normal, csiv2_mech = Mech} = HostData, TaggedProfile, IOR, Ctx) when is_record(Mech, 'CSIIOP_CompoundSecMech') -> connect2([{Host, Port}], Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx); connect(_Host, _Port, _Objkey, _Timeout, _Index, HostData, _TaggedProfile, IOR, _Ctx) -> orber:dbg("[~p] orber_iiop:connect(~p)~n" "Unable to use the supplied IOR.~n" "Connection Data: ~p", [?LINE, IOR, HostData], ?DEBUG_LEVEL), corba:raise(#'INV_OBJREF'{completion_status=?COMPLETED_NO}). connect2(HostPort, Objkey, Timeout, Index, HostData, TaggedProfile, IOR, Ctx) -> case try_connect(HostPort, HostData#host_data.protocol, Timeout, HostData, Ctx) of error -> Alts = iop_ior:get_alt_addr(TaggedProfile), case try_connect(Alts, HostData#host_data.protocol, Timeout, HostData, Ctx) of error -> case iop_ior:get_key(IOR, Index) of undefined -> corba:raise(#'COMM_FAILURE'{completion_status = ?COMPLETED_NO}); {'external', {NewHost, NewPort, NewObjkey, NewIndex, NewTaggedProfile, NewHostData}} -> connect(NewHost, NewPort, NewObjkey, Timeout, [NewIndex|Index], NewHostData, NewTaggedProfile, IOR, Ctx); _What -> orber:dbg("[~p] orber_iiop:connect2(~p)~n" "Illegal IOR; contains a mixture of local and external profiles.", [?LINE, IOR], ?DEBUG_LEVEL), corba:raise(#'INV_OBJREF'{completion_status=?COMPLETED_NO}) end; X -> {X, Objkey, HostData#host_data.version} end; X -> {X, Objkey, HostData#host_data.version} end. try_connect([], _, _, _, _) -> error; try_connect([{Host, Port}|T], SocketType, Timeout, HostData, Ctx) -> case catch orber_iiop_pm:connect(Host, Port, SocketType, Timeout, HostData#host_data.charset, HostData#host_data.wcharset, Ctx) of {ok, P, Ctx2, Int, Interface} -> {P, Ctx2, Int, Interface}; {'EXCEPTION', #'BAD_CONTEXT'{} = CtxExc} -> orber:dbg("[~p] orber_iiop:try_connect(~p, ~p) failed~n", [?LINE, Host, Port], ?DEBUG_LEVEL), corba:raise(CtxExc); {'EXCEPTION', _PMExc} -> try_connect(T, SocketType, Timeout, HostData, Ctx); {'EXIT',{timeout,_}} -> orber:dbg("[~p] orber_iiop:try_connect(~p, ~p, ~p)~n" "Connect attempt timed out", [?LINE, Host, Port, Timeout], ?DEBUG_LEVEL), try_connect(T, SocketType, Timeout, HostData, Ctx); {'EXIT', What} -> orber:dbg("[~p] orber_iiop:try_connect(~p, ~p, ~p)~n" "Connect attempt resulted in: ~p", [?LINE, Host, Port, Timeout, What], ?DEBUG_LEVEL), try_connect(T, SocketType, Timeout, HostData, Ctx) end.