package org.jacorb.orb.iiop; /* * JacORB - a free Java ORB * * Copyright (C) 1997-2014 Gerald Brose / The JacORB Team. * * This library is free software; you can redistribute it and/or modify it under the terms of the * GNU Library General Public License as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License along with this * library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, * USA. */ import java.net.Inet6Address; import java.net.InetAddress; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.SocketException; import java.net.UnknownHostException; import java.util.Collections; import java.util.Enumeration; import java.util.LinkedList; import org.jacorb.config.Configuration; import org.jacorb.config.ConfigurationException; import org.jacorb.orb.CDROutputStream; import org.jacorb.orb.etf.ListenEndpoint.Protocol; import org.jacorb.orb.etf.ProtocolAddressBase; import org.omg.CORBA.INTERNAL; import org.omg.CORBA.ORBSingleton; import org.slf4j.Logger; /** * @author Andre Spiegel, Phil Mesnier */ public class IIOPAddress extends ProtocolAddressBase { private static final String[] networkVirtualInterfaces; static { networkVirtualInterfaces = System.getProperty("jacorb.network.virtual", "VirtualBox,VMWare,VMware,virbr0,vboxnet,docker").split(","); } private String source_name = null; // initializing string private InetAddress host = null; private InetAddress pseudo_host = null; private int port = -1; // 0 .. 65536 // if this address is used as part of an alias, the hostname may be // unresolvable. Thus regardless of the dnsEnabled state, the source // name as given will be reported. private boolean unresolvable = false; private boolean dnsEnabled = false; private boolean hideZoneID = true; private Logger logger; private boolean doEagerResolve = true; private boolean forceDNSLookup = true; private Protocol protocol = null; private boolean isWildcard = false; private boolean isConfigured = false; /** * Creates a new IIOPAddress that will be initialized later by a string */ public IIOPAddress() { super(); } /** * Creates a new IIOPAddress for <code>host</code> and <code>port</code>. * * @param hoststr * either a DNS name, or a textual representation of a numeric IP address (dotted * decimal) * @param port * the port number represented as an integer, in the range 0..65535. As a special * convenience, a negative number is converted by adding 65536 to it; this helps * using values that were previously stored in a Java <code>short</code>. */ public IIOPAddress(String hoststr, int port) { this(); source_name = hoststr; init_port(port); } /** * Method for use by the IIOPListener. Create a new IIOPAddress for <code>serverSocket</code> * which has already been instantiated. * * @param serverSocket */ public IIOPAddress(ServerSocket serverSocket) { this(); /** * Once a Server socket has been instantiated, getInetAddress().toString() would return a * string in the form "hostname/hostaddress". Note that hostname and hostaddress may be the * same. So, the following code segment would extract the hostname from the returned * inetAddress. */ setPort(serverSocket.getLocalPort()); setHostInetAddress(serverSocket.getInetAddress()); // Set the isConfigured flag to prevent calling init_host() later isConfigured = true; } private void init_port(int port) { if (port < 0) { this.port = port + 65536; } else { this.port = port; } } @Override public void configure(Configuration configuration) throws ConfigurationException { super.configure(configuration); logger = this.configuration.getLogger("org.jacorb.iiop.address"); dnsEnabled = configuration.getAttributeAsBoolean("jacorb.dns.enable", false); hideZoneID = configuration.getAttributeAsBoolean("jacorb.ipv6.hide_zoneid", true); doEagerResolve = configuration.getAttributeAsBoolean("jacorb.dns.eager_resolve", true); forceDNSLookup = configuration.getAttributeAsBoolean("jacorb.dns.force_lookup", true); /** * Check if this object has already been configured. See IIOPAddress (ServerSocket) */ if (isConfigured == true) { return; } if (doEagerResolve) { init_host(); } // Set the isConfigured flag isConfigured = true; } /** * The InetAddress class can handle both IPv4 and IPv6 addresses. If the address is not in a * valid format, an exception is thrown. For this reason, isIP() is no longer needed. */ private void init_host() { if (source_name == null || source_name.length() == 0) { /** * Setting host to null to indicate wildcard host so that when the ServerSocket function * is called, the system will create a wildcard listener that would listen on all * listenable network interfaces. */ host = null; } else { int slash = source_name.indexOf('/'); if (slash > 0) { // fixes two problems: // 1) if the user specified the network bits, // 2) if the user used the name/ip format source_name = source_name.substring(0, slash); } try { host = InetAddress.getByName(source_name); } catch (UnknownHostException ex) { if (logger.isDebugEnabled()) { logger.debug("init_host, " + source_name + " unresolvable"); } unresolvable = true; try { // Attempt to fallback to some valid IP address. host = InetAddress.getLocalHost(); if (logger.isDebugEnabled()) { logger.debug("init_host, " + "default to " + host.toString()); } } catch (UnknownHostException ex2) { } } } } // This is called by TaggedComponentList.getComponents from IIOPProfile. public static IIOPAddress read(org.omg.CORBA.portable.InputStream in) { String host = in.read_string(); short port = in.read_ushort(); return new IIOPAddress(host, port); } public void setProtocol(Protocol proto) { this.protocol = proto; } public Protocol getProtocol() { return this.protocol; } /** * Returns the host part of this IIOPAddress, as a numeric IP address in dotted decimal form. If * the numeric IP address was specified when this object was created, then that address is * returned. Otherwise, this method performs a DNS lookup on the hostname. */ public String getIP() { String result = null; if (host == null) { init_host(); } if (unresolvable || host == null) { return source_name; } if (!dnsEnabled) { if (!isWildcard()) { result = host.getHostAddress(); } else if (pseudo_host != null) { result = pseudo_host.getHostAddress(); } } else { if (!isWildcard()) { result = forceDNSLookup ? host.getCanonicalHostName() : host.getHostName(); } else if (pseudo_host != null) { result = forceDNSLookup ? pseudo_host.getCanonicalHostName() : pseudo_host.getHostName(); } } return processZoneID(result); } /** * Returns the host part of this IIOPAddress, as a DNS hostname. If the DNS name was specified * when this IIOPAddress was created, then that name is returned. Otherwise, this method * performs a reverse DNS lookup on the IP address. */ public String getHostName() { if (host == null) { init_host(); } if (unresolvable || host == null) { return source_name; } if (!isWildcard()) { return processZoneID(dnsEnabled ? host.getCanonicalHostName() : host.getHostAddress()); } else if (pseudo_host != null) { return processZoneID(dnsEnabled ? pseudo_host.getCanonicalHostName() : pseudo_host.getHostAddress()); } // should not get here return null; } /** * Hide the zoneID if hideZoneID is true * @param source * @return */ private String processZoneID (String source) { if (hideZoneID) { int zoneIndex; if ((zoneIndex = source.indexOf('%')) != -1) { source = source.substring(0, zoneIndex); } } return source; } /** * Used by the ORB to configure just the hostname portion of a proxy IOR address */ public void setHostname(String hn) { host = null; source_name = hn; if (doEagerResolve) { init_host(); } } /** * Returns the host as supplied to the constructor. This replaces * IIOPListener.getConfiguredHost(). * Used by the IIOPListener to retrieve the host address for a wildcard listener after * the server socket has been instantiated. */ public InetAddress getConfiguredHost() { if (source_name == null || source_name.length() == 0) { return null; } if (host == null) { init_host(); } return host; } /** * Returns the port number of this address, represented as an integer in the range 0..65535. */ public int getPort() { return port; } public void setPort(int p) { port = p; } /** * Method for use by the IIOPListener to set host address for a wildcard listener after the * server socket has been instantiated. The flag isWildcard and the source_name will be updated * to reflect the current state of the wildcard listener. * * @param hostInetAddr */ public void setHostInetAddress(InetAddress hostInetAddr) { if (host == null) { host = hostInetAddr; isWildcard = host.isAnyLocalAddress(); if (isWildcard) { pseudo_host = getLocalHost(); source_name = pseudo_host.getHostName(); } else { source_name = host.toString(); int slash_delim = source_name.indexOf('/'); if (slash_delim > 0) { source_name = source_name.substring(0, slash_delim); } } source_name = processZoneID(source_name); } } /** * * @return the boolean state of the wildcard listener. A true state indicates a wildcard * listener. */ public boolean isWildcard() { return isWildcard; } /** * Method for use by the IIOPListener to set the wildcard state of a wildcard listener after the * server socket has been instantiated. * * @param state */ public void setWildcardHost(boolean state) { isWildcard = state; } @Override public boolean equals(Object other) { if (other instanceof IIOPAddress) { return toString().equals(other.toString()); } return false; } @Override public int hashCode() { return toString().hashCode(); } @Override public String toString() { return getHostName() + ":" + port; } @Override public boolean fromString(String s) { if (s.charAt(0) == '[') { return fromStringIPv6(s); } return fromStringIPv4(s); } // NOTE: IPv6 format is "[address]:port" since address will include colons. private boolean fromStringIPv6(String s) { int end_bracket = s.indexOf(']'); if (end_bracket < 0) { return false; } // In case the IIOPAddress object is created using the host inetAddress // as the source_name which normally contains the routing zone id // delimted // by the percent (%). As such, it needs to be removed. int route_delim = s.lastIndexOf('%', end_bracket); if (route_delim < 0) { source_name = s.substring(1, end_bracket); } else { source_name = s.substring(1, route_delim); } int port_colon = s.indexOf(':', end_bracket); if (port_colon < 0) { return false; } int _port = Integer.parseInt(s.substring(port_colon + 1)); init_host(); init_port(_port); return true; } private boolean fromStringIPv4(String s) { int colon = s.indexOf(':'); if (colon == -1) { return false; } if (colon > 0) { source_name = s.substring(0, colon); } else { source_name = ""; } int _port = 0; if (colon < s.length() - 1) { _port = Integer.parseInt(s.substring(colon + 1)); } init_host(); init_port(_port); return true; } @Override public void write(CDROutputStream cdr) { // If host name contains a zone ID, we need to remove it. // This would be used to write the address on an IOR or other // things that could be used off-host. Writing a link-local zone // ID would break the client. Site-local zone IDs are still used, // but deprecated. For now, we will ignore site-local zone IDs. cdr.write_string(getHostName()); cdr.write_ushort((short) port); } /** * Method for use by the PrintIOR utility. Previously it called getHostname() which may or may * not have returned what was actually encoded in the IOR. This is of limited use for debugging * purposes. This method attempts to return the string that this address was actually * constructed with (i.e. what the IOR actually contains as its host string). * * @return Host name or IP address or both if the original host string cannot be determined. */ public String getOriginalHost() { if (source_name == null) { if (!dnsEnabled) { return getIP(); } return getHostName() + " / " + getIP(); } return source_name; } /** * Package level method used by IIOPProfile to cause selective replacement of either the * hostname or the port or both */ void replaceFrom(IIOPAddress other) { if (other.source_name != null) { setHostname(other.source_name); } if (other.port != -1) { setPort(other.port); } } /** * Returns a string representation of the localhost address. */ public static String getLocalHostAddress(Logger logger) { InetAddress addr = getLocalHost(); if (addr != null) { return addr.getHostAddress(); } else { logger.warn("Unable to resolve local IP address - using default"); return "127.0.0.1"; } } /** * Returns an address for the localhost that is reasonable to use in the IORs we produce. */ public static InetAddress getLocalHost() { return getNetworkInetAddresses().getFirst(); } /** * Returns an ordered list of InetAddresses. Order is: * * IPv4/IPv6 routable address Point-to-point address Fallback to link-local and finally loopback. * */ public static LinkedList<InetAddress> getNetworkInetAddresses() { LinkedList<InetAddress> result = new LinkedList<InetAddress>(); LinkedList<InetAddress> virtual = new LinkedList<InetAddress>(); LinkedList<InetAddress> p2plinklocal = new LinkedList<InetAddress>(); LinkedList<InetAddress> loopback = new LinkedList<InetAddress>(); // Its somewhat tricky in Java to return the default route (i.e. what ip // route show would provide). // Its also possible that a VirtualBox/VMWare or Docker interface would // get returned // before an Ethernet/WLAN interface. As those may not be routeable the // JVM System Property // jacorb.network.virtual may be used to deprioritise those in the list. // // https://stackoverflow.com/questions/8219664/java-gethostaddress-returning-virtualbox-ipv4-address // https://stackoverflow.com/questions/7348711/recommended-way-to-get-hostname-in-java/7353473#7353473 // https://stackoverflow.com/questions/11797641/java-finding-network-interface-for-default-gateway // http://ireasoning.com/articles/find_local_ip_address.htm try { for (NetworkInterface ni : Collections.list(NetworkInterface.getNetworkInterfaces())) { boolean isVirtual = false; for (String nvi : networkVirtualInterfaces) { String displayName = ni.getDisplayName(); if (displayName != null && displayName.contains(nvi)) { isVirtual = true; break; } } if (ni.isPointToPoint()) { Enumeration<InetAddress> addr = ni.getInetAddresses(); while (addr.hasMoreElements()) { p2plinklocal.addFirst(addr.nextElement()); } } else if (isVirtual) { Enumeration<InetAddress> addrList = ni.getInetAddresses(); while (addrList.hasMoreElements()) { InetAddress addr = addrList.nextElement(); if (!addr.isLoopbackAddress() && !addr.isLinkLocalAddress()) { virtual.addLast(addr); } else if (addr.isLinkLocalAddress()) { p2plinklocal.addLast(addr); } else { loopback.add(addr); } } } else { Enumeration<InetAddress> addrList = ni.getInetAddresses(); while (addrList.hasMoreElements()) { InetAddress addr = addrList.nextElement(); if (!addr.isLoopbackAddress() && !addr.isLinkLocalAddress()) { if (addr instanceof Inet6Address) { result.addLast(addr); } else { result.addFirst(addr); } } else if (addr.isLinkLocalAddress()) { p2plinklocal.addLast(addr); } else { loopback.add(addr); } } } } } catch (SocketException se) { ((org.jacorb.orb.ORBSingleton) ORBSingleton.init()).getLogger().error( "Unable to determine network interfaces", se); throw new INTERNAL("Unable to determine network interfaces: " + se); } result.addAll(virtual); result.addAll(p2plinklocal); result.addAll(loopback); return result; } @Override public ProtocolAddressBase copy() { IIOPAddress result = new IIOPAddress(getHostName(), port); result.logger = logger; return result; } }