2 #include "tao/IIOP_Profile.h"
4 #if defined (TAO_HAS_IIOP) && (TAO_HAS_IIOP != 0)
6 #include "tao/ORB_Core.h"
8 #include "tao/IIOP_EndpointsC.h"
10 #include "tao/SystemException.h"
11 #include "ace/OS_NS_string.h"
12 #include "ace/OS_NS_stdio.h"
13 #include "ace/Truncate.h"
14 #include "ace/os_include/os_netdb.h"
17 static const char the_prefix
[] = "iiop";
19 TAO_BEGIN_VERSIONED_NAMESPACE_DECL
21 TAO_IIOP_Profile::~TAO_IIOP_Profile ()
23 // Clean up the list of endpoints since we own it.
24 // Skip the head, since it is not dynamically allocated.
25 TAO_Endpoint
*tmp
= nullptr;
27 for (TAO_Endpoint
*next
= this->endpoint ()->next ();
36 const char TAO_IIOP_Profile::object_key_delimiter_
= '/';
39 TAO_IIOP_Profile::object_key_delimiter () const
41 return TAO_IIOP_Profile::object_key_delimiter_
;
44 TAO_IIOP_Profile::TAO_IIOP_Profile (const ACE_INET_Addr
&addr
,
45 const TAO::ObjectKey
&object_key
,
46 const TAO_GIOP_Message_Version
&version
,
47 TAO_ORB_Core
*orb_core
)
48 : TAO_Profile (IOP::TAG_INTERNET_IOP
,
53 orb_core
->orb_params ()->use_dotted_decimal_addresses ()),
54 last_endpoint_ (&this->endpoint_
),
59 TAO_IIOP_Profile::TAO_IIOP_Profile (const char* host
,
61 const TAO::ObjectKey
&object_key
,
62 const ACE_INET_Addr
&addr
,
63 const TAO_GIOP_Message_Version
&version
,
64 TAO_ORB_Core
*orb_core
)
65 : TAO_Profile (IOP::TAG_INTERNET_IOP
,
69 endpoint_ (host
, port
, addr
),
70 last_endpoint_ (&this->endpoint_
),
75 TAO_IIOP_Profile::TAO_IIOP_Profile (TAO_ORB_Core
*orb_core
)
76 : TAO_Profile (IOP::TAG_INTERNET_IOP
,
78 TAO_GIOP_Message_Version (TAO_DEF_GIOP_MAJOR
,
81 last_endpoint_ (&this->endpoint_
),
87 TAO_IIOP_Profile::decode_profile (TAO_InputCDR
& cdr
)
89 // Decode host and port into the <endpoint_>.
90 // it is necessary to do it indirectly so that IPv6 host addresses
91 // can be evaluated correctly.
92 CORBA::String_var host
;
95 if (cdr
.read_string(host
.out()) == 0 ||
96 cdr
.read_ushort (port
) == 0)
98 if (TAO_debug_level
> 0)
99 TAOLIB_DEBUG ((LM_DEBUG
,
100 ACE_TEXT ("TAO (%P|%t) IIOP_Profile::decode - ")
101 ACE_TEXT ("error while decoding host/port\n")));
105 this->endpoint_
.host(host
.in());
106 this->endpoint_
.port(port
);
110 // Invalidate the object_addr_ until first access.
111 this->endpoint_
.object_addr_
.set_type (-1);
113 const char* csv
= this->orb_core()->orb_params()->preferred_interfaces();
114 if (csv
!= nullptr && *csv
!= '\0')
117 this->orb_core()->orb_params()->enforce_pref_interfaces();
118 this->count_
+= this->endpoint_
.preferred_interfaces(csv
, enforce
, *this);
127 TAO_IIOP_Profile::parse_string_i (const char *ior
)
129 // Pull off the "hostname:port/" part of the objref
130 // Copy the string because we are going to modify it...
132 const char *okd
= std::strchr (ior
, this->object_key_delimiter_
);
134 if (okd
== nullptr || okd
== ior
)
136 // No object key delimiter or no hostname specified.
137 throw ::CORBA::INV_OBJREF (
138 CORBA::SystemException::_tao_minor_code (
141 CORBA::COMPLETED_NO
);
144 // Length of host string.
145 CORBA::ULong length_host
= 0;
147 const char *cp_pos_overrun
= std::strchr (ior
, ':'); // Look for a port
148 const char *cp_pos
= (cp_pos_overrun
< okd
) ? cp_pos_overrun
: nullptr; // but before object key
149 #if defined (ACE_HAS_IPV6)
150 // IPv6 numeric address in host string?
151 bool ipv6_in_host
= false;
153 // Check if this is a (possibly) IPv6 supporting profile containing a
154 // decimal IPv6 address representation.
155 if ((this->version().major
> TAO_MIN_IPV6_IIOP_MAJOR
||
156 this->version().minor
>= TAO_MIN_IPV6_IIOP_MINOR
) &&
159 // In this case we have to find the end of the numeric address and
160 // start looking for the port separator from there.
161 const char *cp_pos_a_overrun
= std::strchr(ior
, ']');
162 const char *cp_pos_a
= (cp_pos_a_overrun
< okd
) ? cp_pos_a_overrun
: 0; // before object key
165 // No valid IPv6 address specified.
166 if (TAO_debug_level
> 0)
168 TAOLIB_ERROR ((LM_ERROR
,
169 ACE_TEXT ("\nTAO (%P|%t) IIOP_Profile: ")
170 ACE_TEXT ("Invalid IPv6 decimal address specified.\n")));
173 throw ::CORBA::INV_OBJREF (
174 CORBA::SystemException::_tao_minor_code (
177 CORBA::COMPLETED_NO
);
181 if (cp_pos_a
[1] == ':') // Look for a port
182 cp_pos
= cp_pos_a
+ 1;
185 ipv6_in_host
= true; // host string contains full IPv6 numeric address
188 #endif /* ACE_HAS_IPV6 */
192 // No hostname, however one is required by the spec when specifying a port.
193 // See formal-04-03-01, section 13.6.10.3
194 if (TAO_debug_level
> 0)
196 TAOLIB_ERROR ((LM_ERROR
,
197 ACE_TEXT ("\nTAO (%P|%t) IIOP_Profile: ")
198 ACE_TEXT ("Host address may be omited only when no port has been specified.\n")));
201 throw ::CORBA::INV_OBJREF (
202 CORBA::SystemException::_tao_minor_code (
205 CORBA::COMPLETED_NO
);
207 else if (cp_pos
!= nullptr)
209 // A port number or port name was specified.
210 CORBA::ULong length_port
= ACE_Utils::truncate_cast
<CORBA::ULong
> (okd
- cp_pos
- 1);
211 CORBA::String_var tmp
= CORBA::string_alloc (length_port
);
213 if (tmp
.in() != nullptr)
215 ACE_OS::strncpy (tmp
.inout (), cp_pos
+ 1, length_port
);
216 tmp
[length_port
] = '\0';
219 if (length_port
== 0)
221 this->endpoint_
.port_
= 2809; // default IIOP port for
222 // parsing corbaloc strings
224 else if (tmp
.in () != nullptr && ACE_OS::strspn (tmp
.in (), "1234567890") == length_port
)
226 this->endpoint_
.port_
=
227 static_cast<CORBA::UShort
> (ACE_OS::atoi (tmp
.in ()));
232 if (tmp
.in () == nullptr || ia
.string_to_addr (tmp
.in ()) == -1)
234 throw ::CORBA::INV_OBJREF (
235 CORBA::SystemException::_tao_minor_code (
238 CORBA::COMPLETED_NO
);
242 this->endpoint_
.port_
= ia
.get_port_number ();
245 length_host
= ACE_Utils::truncate_cast
<CORBA::ULong
> (cp_pos
- ior
);
248 length_host
= ACE_Utils::truncate_cast
<CORBA::ULong
> (okd
- ior
);
250 #if defined (ACE_HAS_IPV6)
252 length_host
-= 2; // don't store '[' and ']'
253 #endif /* ACE_HAS_IPV6 */
255 CORBA::String_var tmp
= CORBA::string_alloc (length_host
);
257 #if defined (ACE_HAS_IPV6)
259 ACE_OS::strncpy (tmp
.inout (), ior
+ 1, length_host
);
261 #endif /* ACE_HAS_IPV6 */
262 // Skip the trailing '/'
263 ACE_OS::strncpy (tmp
.inout (), ior
, length_host
);
264 tmp
[length_host
] = '\0';
266 this->endpoint_
.host_
= tmp
._retn ();
267 #if defined (ACE_HAS_IPV6)
268 this->endpoint_
.is_ipv6_decimal_
= ipv6_in_host
;
269 #endif /* ACE_HAS_IPV6 */
271 if (ACE_OS::strcmp (this->endpoint_
.host_
.in (), "") == 0)
273 ACE_INET_Addr host_addr
;
275 char tmp_host
[MAXHOSTNAMELEN
+ 1];
277 // If no host is specified: assign the default host, i.e. the
279 if (host_addr
.get_host_name (tmp_host
,
280 sizeof (tmp_host
)) != 0)
282 // Can't get the IP address since the INET_Addr wasn't
283 // initialized. Just throw an exception.
285 if (TAO_debug_level
> 0)
286 TAOLIB_DEBUG ((LM_DEBUG
,
287 ACE_TEXT ("\n\nTAO (%P|%t) ")
288 ACE_TEXT ("IIOP_Profile::parse_string ")
289 ACE_TEXT ("- %p\n\n"),
290 ACE_TEXT ("cannot determine hostname")));
292 // @@ What's the right exception to throw here?
293 throw ::CORBA::INV_OBJREF (
294 CORBA::SystemException::_tao_minor_code (
297 CORBA::COMPLETED_NO
);
300 this->endpoint_
.host_
= CORBA::string_dup (tmp_host
);
301 const char* csv
= this->orb_core()->orb_params()->preferred_interfaces();
303 this->orb_core()->orb_params()->enforce_pref_interfaces();
304 this->endpoint_
.preferred_interfaces (csv
, enforce
, *this);
309 TAO::ObjectKey::decode_string_to_sequence (ok
,
312 (void) this->orb_core ()->object_key_table ().bind (ok
,
313 this->ref_object_key_
);
317 TAO_IIOP_Profile::do_is_equivalent (const TAO_Profile
*other_profile
)
319 if (other_profile
== this)
322 const TAO_IIOP_Profile
*op
=
323 dynamic_cast<const TAO_IIOP_Profile
*> (other_profile
);
325 // Make sure we have a TAO_IIOP_Profile.
329 if (this->count_
== 0 && op
->count_
== 0)
331 if (this->count_
!= op
->count_
)
333 // Check endpoints equivalence.
334 const TAO_IIOP_Endpoint
*other_endp
= &op
->endpoint_
;
335 for (TAO_IIOP_Endpoint
*endp
= &this->endpoint_
;
339 if (endp
->is_equivalent (other_endp
))
340 other_endp
= other_endp
->next_
;
349 TAO_IIOP_Profile::hash (CORBA::ULong max
)
351 // Get the hash value for all endpoints.
352 CORBA::ULong hashval
= 0;
353 for (TAO_IIOP_Endpoint
*endp
= &this->endpoint_
;
357 hashval
+= endp
->hash ();
360 hashval
+= this->version_
.minor
;
361 hashval
+= this->tag ();
363 const TAO::ObjectKey
&ok
=
364 this->ref_object_key_
->object_key ();
366 if (ok
.length () >= 4)
372 hashval
+= TAO_Profile::hash_service_i (max
);
374 return hashval
% max
;
378 TAO_IIOP_Profile::endpoint ()
380 return &this->endpoint_
;
384 TAO_IIOP_Profile::base_endpoint ()
386 // do not call endpoint(), return the value directly. This is to
387 // avoid calling a derived implementation of endpoint().
388 return &this->endpoint_
;
392 TAO_IIOP_Profile::endpoint_count () const
398 TAO_IIOP_Profile::add_endpoint (TAO_IIOP_Endpoint
*endp
)
400 this->last_endpoint_
->next_
= endp
;
401 this->last_endpoint_
= endp
;
407 TAO_IIOP_Profile::remove_endpoint (TAO_IIOP_Endpoint
*endp
)
412 // special handling for the target matching the base endpoint
413 if (endp
== &this->endpoint_
)
415 if (--this->count_
> 0)
417 TAO_IIOP_Endpoint
* n
= this->endpoint_
.next_
;
418 this->endpoint_
= *n
;
419 // since the assignment operator does not copy the next_
420 // pointer, we must do it by hand
421 this->endpoint_
.next_
= n
->next_
;
422 if (this->last_endpoint_
== n
)
424 this->last_endpoint_
= &this->endpoint_
;
431 TAO_IIOP_Endpoint
* prev
= &this->endpoint_
;
432 TAO_IIOP_Endpoint
* cur
= this->endpoint_
.next_
;
434 while (cur
!= nullptr)
444 prev
->next_
= cur
->next_
;
445 cur
->next_
= nullptr;
447 if (this->last_endpoint_
== cur
)
449 this->last_endpoint_
= prev
;
456 TAO_IIOP_Profile::remove_generic_endpoint (TAO_Endpoint
*ep
)
458 this->remove_endpoint(dynamic_cast<TAO_IIOP_Endpoint
*>(ep
));
462 TAO_IIOP_Profile::add_generic_endpoint (TAO_Endpoint
*endp
)
464 TAO_IIOP_Endpoint
*iep
= dynamic_cast<TAO_IIOP_Endpoint
*>(endp
);
467 TAO_IIOP_Endpoint
*clone
;
468 ACE_NEW (clone
, TAO_IIOP_Endpoint(*iep
));
469 this->add_endpoint(clone
);
474 TAO_IIOP_Profile::to_string () const
476 // corbaloc:iiop:1.2@host:port,iiop:1.2@host:port,.../key
478 CORBA::String_var key
;
479 TAO::ObjectKey::encode_sequence_to_string (key
.inout(),
480 this->ref_object_key_
->object_key ());
484 1 /* colon separator */ +
485 1 /* object key separator */ +
486 std::strlen (key
.in ()));
487 size_t const pfx_len
= (
488 std::strlen (::the_prefix
) /* "iiop" */ +
489 1 /* colon separator */);
491 const TAO_IIOP_Endpoint
*endp
= nullptr;
492 for (endp
= &this->endpoint_
; endp
!= nullptr; endp
= endp
->next_
)
496 1 /* major version */ +
497 1 /* decimal point */ +
498 1 /* minor version */ +
499 1 /* `@' character */ +
500 std::strlen (endp
->host ()) +
501 1 /* colon separator */ +
502 5 /* port number */ +
504 #if defined (ACE_HAS_IPV6)
505 if (endp
->is_ipv6_decimal_
)
506 buflen
+= 2; // room for '[' and ']'
507 #endif /* ACE_HAS_IPV6 */
510 static const char digits
[] = "0123456789";
512 char * buf
= CORBA::string_alloc (static_cast<CORBA::ULong
> (buflen
));
514 ACE_OS::strcpy(buf
, "corbaloc:");
516 for (endp
= &this->endpoint_
; endp
!= nullptr; endp
= endp
->next_
)
518 if(&this->endpoint_
!= endp
)
519 ACE_OS::strcat(buf
, ",");
521 #if defined (ACE_HAS_IPV6)
522 if (endp
->is_ipv6_decimal_
)
524 // Don't publish scopeid if included.
525 ACE_CString
tmp(endp
->host ());
526 ACE_CString::size_type pos
= tmp
.find('%');
527 if (pos
!= ACE_CString::npos
)
529 tmp
= tmp
.substr(0, pos
+ 1);
532 ACE_OS::sprintf (buf
+ std::strlen(buf
),
535 digits
[this->version_
.major
],
536 digits
[this->version_
.minor
],
542 ACE_OS::sprintf (buf
+ std::strlen(buf
),
545 digits
[this->version_
.major
],
546 digits
[this->version_
.minor
],
551 ACE_OS::sprintf (buf
+ std::strlen(buf
),
553 this->object_key_delimiter_
,
560 TAO_IIOP_Profile::prefix ()
566 TAO_IIOP_Profile::create_profile_body (TAO_OutputCDR
&encap
) const
568 encap
.write_octet (TAO_ENCAP_BYTE_ORDER
);
571 encap
.write_octet (this->version_
.major
);
572 encap
.write_octet (this->version_
.minor
);
574 // STRING hostname from profile
575 #if defined (ACE_HAS_IPV6)
576 // For IPv6 decimal addresses make sure the possibly included scopeid
577 // is not published as this has only local meaning.
578 const char* host
= 0;
580 if (this->endpoint_
.is_ipv6_decimal_
&&
581 (pos
= std::strchr (host
= this->endpoint_
.host (), '%')) != 0)
584 size_t len
= pos
- host
;
585 tmp
.set (this->endpoint_
.host (), len
, 1);
586 encap
.write_string (tmp
.c_str ());
589 #endif /* ACE_HAS_IPV6 */
590 encap
.write_string (this->endpoint_
.host ());
592 // UNSIGNED SHORT port number
593 encap
.write_ushort (this->endpoint_
.port ());
595 // OCTET SEQUENCE for object key
596 if (this->ref_object_key_
)
597 encap
<< this->ref_object_key_
->object_key ();
600 TAOLIB_ERROR ((LM_ERROR
,
601 "(%P|%t) TAO - IIOP_Profile::create_profile_body "
602 "no object key marshalled\n"));
605 if (this->version_
.major
> 1 || this->version_
.minor
> 0)
606 this->tagged_components ().encode (encap
);
610 TAO_IIOP_Profile::encode_alternate_endpoints ()
612 // encode IOP::TAG_ALTERNATE_IIOP_ADDRESS tags if there are more
613 // than one endpoints to listen to.
614 const TAO_IIOP_Endpoint
*endpoint
= &this->endpoint_
;
615 for (CORBA::ULong i
= 1;
619 // The first endpoint is the actual endpoint. The rest of the
620 // endpoints are the alternate endpoints. So, neglect the first
621 // endpoint for TAG_ALTERNATE_IIOP_ADDRESS
622 endpoint
= endpoint
->next_
;
624 if (!endpoint
->is_encodable_
)
627 // Encode the data structure. - The CORBA specification does not
628 // mandate a particular container for the endpoints, only that
629 // it is encoded as host first, then port.
630 TAO_OutputCDR out_cdr
;
632 #if defined (ACE_HAS_IPV6)
633 // For IPv6 decimal addresses make sure the possibly included scopeid
634 // is not published as this has only local meaning.
635 const char* host
= 0;
637 if (endpoint
->is_ipv6_decimal_
&&
638 (pos
= std::strchr (host
= endpoint
->host (), '%')) != 0)
641 size_t len
= pos
- host
;
642 tmp
.set (endpoint
->host (), len
, 1);
643 if (!(out_cdr
<< ACE_OutputCDR::from_boolean (TAO_ENCAP_BYTE_ORDER
))
644 || !(out_cdr
<< tmp
.c_str ())
645 || !(out_cdr
<< endpoint
->port ()))
647 out_cdr
.write_string (len
, endpoint
->host ());
650 #endif /* ACE_HAS_IPV6 */
651 if (!(out_cdr
<< ACE_OutputCDR::from_boolean (TAO_ENCAP_BYTE_ORDER
))
652 || !(out_cdr
<< endpoint
->host ())
653 || !(out_cdr
<< endpoint
->port ()))
656 IOP::TaggedComponent tagged_component
;
657 tagged_component
.tag
= IOP::TAG_ALTERNATE_IIOP_ADDRESS
;
659 size_t length
= out_cdr
.total_length ();
660 tagged_component
.component_data
.length
661 (static_cast<CORBA::ULong
>(length
));
663 tagged_component
.component_data
.get_buffer ();
665 for (const ACE_Message_Block
*iterator
= out_cdr
.begin ();
667 iterator
= iterator
->cont ())
669 size_t i_length
= iterator
->length ();
670 ACE_OS::memcpy (buf
, iterator
->rd_ptr (), i_length
);
675 // Add component with encoded endpoint data to this profile's
677 tagged_components_
.set_component (tagged_component
);
683 TAO_IIOP_Profile::encode_endpoints ()
685 CORBA::ULong actual_count
= 0;
687 const TAO_IIOP_Endpoint
*endpoint
= &this->endpoint_
;
689 // Count the number of endpoints that needs to be encoded
690 for (CORBA::ULong c
= 0;
694 if (endpoint
->is_encodable_
)
697 endpoint
= endpoint
->next_
;
700 // Create a data structure and fill it with endpoint info for wire
702 // We include information for the head of the list
703 // together with other endpoints because even though its addressing
704 // info is transmitted using standard ProfileBody components, its
707 TAO::IIOPEndpointSequence endpoints
;
708 endpoints
.length (actual_count
);
710 endpoint
= &this->endpoint_
;
712 for (CORBA::ULong i
= 0;
716 if (endpoint
->is_encodable_
)
718 #if defined (ACE_HAS_IPV6)
719 if (endpoint
->is_ipv6_decimal_
)
721 // Don't publish scopeid if included.
722 ACE_CString
tmp(endpoint
->host ());
723 ACE_CString::size_type pos
= tmp
.find('%');
724 if (pos
!= ACE_CString::npos
)
726 tmp
= tmp
.substr (0, pos
+ 1);
728 endpoints
[i
].host
= tmp
.c_str();
731 endpoints
[i
].host
= tmp
.c_str();
734 #endif /* ACE_HAS_IPV6 */
735 endpoints
[i
].host
= endpoint
->host ();
736 endpoints
[i
].port
= endpoint
->port ();
737 endpoints
[i
].priority
= endpoint
->priority ();
739 endpoint
= endpoint
->next_
;
742 // Encode the data structure.
743 TAO_OutputCDR out_cdr
;
744 if (!(out_cdr
<< ACE_OutputCDR::from_boolean (TAO_ENCAP_BYTE_ORDER
))
745 || !(out_cdr
<< endpoints
))
748 this->set_tagged_components (out_cdr
);
755 TAO_IIOP_Profile::decode_endpoints ()
757 IOP::TaggedComponent tagged_component
;
758 tagged_component
.tag
= TAO_TAG_ENDPOINTS
;
760 if (this->tagged_components_
.get_component (tagged_component
))
762 const CORBA::Octet
*buf
=
763 tagged_component
.component_data
.get_buffer ();
765 TAO_InputCDR
in_cdr (reinterpret_cast<const char *> (buf
),
766 tagged_component
.component_data
.length ());
768 // Extract the Byte Order.
769 CORBA::Boolean byte_order
;
770 if (!(in_cdr
>> ACE_InputCDR::to_boolean (byte_order
)))
772 in_cdr
.reset_byte_order (static_cast<int> (byte_order
));
774 // Extract endpoints sequence.
775 TAO::IIOPEndpointSequence endpoints
;
777 if (!(in_cdr
>> endpoints
))
780 // Get the priority of the first endpoint (head of the list.
781 // It's other data is extracted as part of the standard profile
783 this->endpoint_
.priority (endpoints
[0].priority
);
785 // Use information extracted from the tagged component to
786 // populate the profile. Skip the first endpoint, since it is
787 // always extracted through standard profile body. Also, begin
788 // from the end of the sequence to preserve endpoint order,
789 // since <add_endpoint> method reverses the order of endpoints
791 for (CORBA::ULong i
= endpoints
.length () - 1;
795 TAO_IIOP_Endpoint
*endpoint
= nullptr;
796 ACE_NEW_RETURN (endpoint
,
797 TAO_IIOP_Endpoint (endpoints
[i
].host
,
799 endpoints
[i
].priority
),
802 this->add_endpoint (endpoint
);
806 // Now decode if there are any TAG_ALTERNATE_IIOP_ADDRESS
809 IOP::MultipleComponentProfile
& tc
= this->tagged_components_
.components();
810 for (CORBA::ULong index
= 0; index
< tc
.length(); index
++)
812 if (tc
[index
].tag
!= IOP::TAG_ALTERNATE_IIOP_ADDRESS
)
814 const CORBA::Octet
*buf
=
815 tc
[index
].component_data
.get_buffer ();
817 TAO_InputCDR
in_cdr (reinterpret_cast<const char*>(buf
),
818 tc
[index
].component_data
.length ());
820 // Extract the Byte Order.
821 CORBA::Boolean byte_order
;
822 if (!(in_cdr
>> ACE_InputCDR::to_boolean (byte_order
)))
825 in_cdr
.reset_byte_order (static_cast<int>(byte_order
));
827 CORBA::String_var host
;
830 if (!(in_cdr
>> host
.out()) || !(in_cdr
>> port
))
833 TAO_IIOP_Endpoint
*endpoint
= nullptr;
834 ACE_NEW_RETURN (endpoint
,
835 TAO_IIOP_Endpoint (host
.in(),
837 TAO_INVALID_PRIORITY
),
840 this->add_endpoint (endpoint
);
846 TAO_END_VERSIONED_NAMESPACE_DECL
848 #endif /* TAO_HAS_IIOP && TAO_HAS_IIOP != 0 */