1 /*
   2  * Copyright (c) 1996, 2012, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.rmi.transport.tcp;
  26 
  27 import java.io.DataInput;
  28 import java.io.DataOutput;
  29 import java.io.IOException;
  30 import java.io.ObjectInput;
  31 import java.io.ObjectOutput;
  32 import java.net.InetAddress;
  33 import java.net.ServerSocket;
  34 import java.net.Socket;
  35 import java.rmi.ConnectIOException;
  36 import java.rmi.RemoteException;
  37 import java.rmi.server.RMIClientSocketFactory;
  38 import java.rmi.server.RMIServerSocketFactory;
  39 import java.rmi.server.RMISocketFactory;
  40 import java.security.AccessController;
  41 import java.security.PrivilegedAction;
  42 import java.util.Collection;
  43 import java.util.HashMap;
  44 import java.util.HashSet;
  45 import java.util.LinkedList;
  46 import java.util.Map;
  47 import java.util.Set;
  48 import sun.rmi.runtime.Log;
  49 import sun.rmi.runtime.NewThreadAction;
  50 import sun.rmi.transport.Channel;
  51 import sun.rmi.transport.Endpoint;
  52 import sun.rmi.transport.Target;
  53 import sun.rmi.transport.Transport;
  54 
  55 /**
  56  * TCPEndpoint represents some communication endpoint for an address
  57  * space (VM).
  58  *
  59  * @author Ann Wollrath
  60  */
  61 public class TCPEndpoint implements Endpoint {
  62     /** IP address or host name */
  63     private String host;
  64     /** port number */
  65     private int port;
  66     /** custom client socket factory (null if not custom factory) */
  67     private final RMIClientSocketFactory csf;
  68     /** custom server socket factory (null if not custom factory) */
  69     private final RMIServerSocketFactory ssf;
  70 
  71     /** if local, the port number to listen on */
  72     private int listenPort = -1;
  73     /** if local, the transport object associated with this endpoint */
  74     private TCPTransport transport = null;
  75 
  76     /** the local host name */
  77     private static String localHost;
  78     /** true if real local host name is known yet */
  79     private static boolean localHostKnown;
  80 
  81     // this should be a *private* method since it is privileged
  82     private static int getInt(String name, int def) {
  83         return AccessController.doPrivileged(
  84                 (PrivilegedAction<Integer>) () -> Integer.getInteger(name, def));
  85     }
  86 
  87     // this should be a *private* method since it is privileged
  88     private static boolean getBoolean(String name) {
  89         return AccessController.doPrivileged(
  90                 (PrivilegedAction<Boolean>) () -> Boolean.getBoolean(name));
  91     }
  92 
  93     /**
  94      * Returns the value of the java.rmi.server.hostname property.
  95      */
  96     private static String getHostnameProperty() {
  97         return AccessController.doPrivileged(
  98             (PrivilegedAction<String>) () -> System.getProperty("java.rmi.server.hostname"));
  99     }
 100 
 101     /**
 102      * Find host name of local machine.  Property "java.rmi.server.hostname"
 103      * is used if set, so server administrator can compensate for the possible
 104      * inablility to get fully qualified host name from VM.
 105      */
 106     static {
 107         localHostKnown = true;
 108         localHost = getHostnameProperty();
 109 
 110         // could try querying CGI program here?
 111         if (localHost == null) {
 112             try {
 113                 InetAddress localAddr = InetAddress.getLocalHost();
 114                 byte[] raw = localAddr.getAddress();
 115                 if ((raw[0] == 127) &&
 116                     (raw[1] ==   0) &&
 117                     (raw[2] ==   0) &&
 118                     (raw[3] ==   1)) {
 119                     localHostKnown = false;
 120                 }
 121 
 122                 /* if the user wishes to use a fully qualified domain
 123                  * name then attempt to find one.
 124                  */
 125                 if (getBoolean("java.rmi.server.useLocalHostName")) {
 126                     localHost = FQDN.attemptFQDN(localAddr);
 127                 } else {
 128                     /* default to using ip addresses, names will
 129                      * work across seperate domains.
 130                      */
 131                     localHost = localAddr.getHostAddress();
 132                 }
 133             } catch (Exception e) {
 134                 localHostKnown = false;
 135                 localHost = null;
 136             }
 137         }
 138 
 139         if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
 140             TCPTransport.tcpLog.log(Log.BRIEF,
 141                 "localHostKnown = " + localHostKnown +
 142                 ", localHost = " + localHost);
 143         }
 144     }
 145 
 146     /** maps an endpoint key containing custom socket factories to
 147      * their own unique endpoint */
 148     // TBD: should this be a weak hash table?
 149     private static final
 150         Map<TCPEndpoint,LinkedList<TCPEndpoint>> localEndpoints =
 151         new HashMap<>();
 152 
 153     /**
 154      * Create an endpoint for a specified host and port.
 155      * This should not be used by external classes to create endpoints
 156      * for servers in this VM; use getLocalEndpoint instead.
 157      */
 158     public TCPEndpoint(String host, int port) {
 159         this(host, port, null, null);
 160     }
 161 
 162     /**
 163      * Create a custom socket factory endpoint for a specified host and port.
 164      * This should not be used by external classes to create endpoints
 165      * for servers in this VM; use getLocalEndpoint instead.
 166      */
 167     public TCPEndpoint(String host, int port, RMIClientSocketFactory csf,
 168                        RMIServerSocketFactory ssf)
 169     {
 170         if (host == null)
 171             host = "";
 172         this.host = host;
 173         this.port = port;
 174         this.csf = csf;
 175         this.ssf = ssf;
 176     }
 177 
 178     /**
 179      * Get an endpoint for the local address space on specified port.
 180      * If port number is 0, it returns shared default endpoint object
 181      * whose host name and port may or may not have been determined.
 182      */
 183     public static TCPEndpoint getLocalEndpoint(int port) {
 184         return getLocalEndpoint(port, null, null);
 185     }
 186 
 187     public static TCPEndpoint getLocalEndpoint(int port,
 188                                                RMIClientSocketFactory csf,
 189                                                RMIServerSocketFactory ssf)
 190     {
 191         /*
 192          * Find mapping for an endpoint key to the list of local unique
 193          * endpoints for this client/server socket factory pair (perhaps
 194          * null) for the specific port.
 195          */
 196         TCPEndpoint ep = null;
 197 
 198         synchronized (localEndpoints) {
 199             TCPEndpoint endpointKey = new TCPEndpoint(null, port, csf, ssf);
 200             LinkedList<TCPEndpoint> epList = localEndpoints.get(endpointKey);
 201             String localHost = resampleLocalHost();
 202 
 203             if (epList == null) {
 204                 /*
 205                  * Create new endpoint list.
 206                  */
 207                 ep = new TCPEndpoint(localHost, port, csf, ssf);
 208                 epList = new LinkedList<TCPEndpoint>();
 209                 epList.add(ep);
 210                 ep.listenPort = port;
 211                 ep.transport = new TCPTransport(epList);
 212                 localEndpoints.put(endpointKey, epList);
 213 
 214                 if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
 215                     TCPTransport.tcpLog.log(Log.BRIEF,
 216                         "created local endpoint for socket factory " + ssf +
 217                         " on port " + port);
 218                 }
 219             } else {
 220                 synchronized (epList) {
 221                     ep = epList.getLast();
 222                     String lastHost = ep.host;
 223                     int lastPort =  ep.port;
 224                     TCPTransport lastTransport = ep.transport;
 225                     // assert (localHost == null ^ lastHost != null)
 226                     if (localHost != null && !localHost.equals(lastHost)) {
 227                         /*
 228                          * Hostname has been updated; add updated endpoint
 229                          * to list.
 230                          */
 231                         if (lastPort != 0) {
 232                             /*
 233                              * Remove outdated endpoints only if the
 234                              * port has already been set on those endpoints.
 235                              */
 236                             epList.clear();
 237                         }
 238                         ep = new TCPEndpoint(localHost, lastPort, csf, ssf);
 239                         ep.listenPort = port;
 240                         ep.transport = lastTransport;
 241                         epList.add(ep);
 242                     }
 243                 }
 244             }
 245         }
 246 
 247         return ep;
 248     }
 249 
 250     /**
 251      * Resamples the local hostname and returns the possibly-updated
 252      * local hostname.
 253      */
 254     private static String resampleLocalHost() {
 255 
 256         String hostnameProperty = getHostnameProperty();
 257 
 258         synchronized (localEndpoints) {
 259             // assert(localHostKnown ^ (localHost == null))
 260 
 261             if (hostnameProperty != null) {
 262                 if (!localHostKnown) {
 263                     /*
 264                      * If the local hostname is unknown, update ALL
 265                      * existing endpoints with the new hostname.
 266                      */
 267                     setLocalHost(hostnameProperty);
 268                 } else if (!hostnameProperty.equals(localHost)) {
 269                     /*
 270                      * Only update the localHost field for reference
 271                      * in future endpoint creation.
 272                      */
 273                     localHost = hostnameProperty;
 274 
 275                     if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
 276                         TCPTransport.tcpLog.log(Log.BRIEF,
 277                             "updated local hostname to: " + localHost);
 278                     }
 279                 }
 280             }
 281             return localHost;
 282         }
 283     }
 284 
 285     /**
 286      * Set the local host name, if currently unknown.
 287      */
 288     static void setLocalHost(String host) {
 289         // assert (host != null)
 290 
 291         synchronized (localEndpoints) {
 292             /*
 293              * If host is not known, change the host field of ALL
 294              * the local endpoints.
 295              */
 296             if (!localHostKnown) {
 297                 localHost = host;
 298                 localHostKnown = true;
 299 
 300                 if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
 301                     TCPTransport.tcpLog.log(Log.BRIEF,
 302                         "local host set to " + host);
 303                 }
 304                 for (LinkedList<TCPEndpoint> epList : localEndpoints.values())
 305                 {
 306                     synchronized (epList) {
 307                         for (TCPEndpoint ep : epList) {
 308                             ep.host = host;
 309                         }
 310                     }
 311                 }
 312             }
 313         }
 314     }
 315 
 316     /**
 317      * Set the port of the (shared) default endpoint object.
 318      * When first created, it contains port 0 because the transport
 319      * hasn't tried to listen to get assigned a port, or if listening
 320      * failed, a port hasn't been assigned from the server.
 321      */
 322     static void setDefaultPort(int port, RMIClientSocketFactory csf,
 323                                RMIServerSocketFactory ssf)
 324     {
 325         TCPEndpoint endpointKey = new TCPEndpoint(null, 0, csf, ssf);
 326 
 327         synchronized (localEndpoints) {
 328             LinkedList<TCPEndpoint> epList = localEndpoints.get(endpointKey);
 329 
 330             synchronized (epList) {
 331                 int size = epList.size();
 332                 TCPEndpoint lastEp = epList.getLast();
 333 
 334                 for (TCPEndpoint ep : epList) {
 335                     ep.port = port;
 336                 }
 337                 if (size > 1) {
 338                     /*
 339                      * Remove all but the last element of the list
 340                      * (which contains the most recent hostname).
 341                      */
 342                     epList.clear();
 343                     epList.add(lastEp);
 344                 }
 345             }
 346 
 347             /*
 348              * Allow future exports to use the actual bound port
 349              * explicitly (see 6269166).
 350              */
 351             TCPEndpoint newEndpointKey = new TCPEndpoint(null, port, csf, ssf);
 352             localEndpoints.put(newEndpointKey, epList);
 353 
 354             if (TCPTransport.tcpLog.isLoggable(Log.BRIEF)) {
 355                 TCPTransport.tcpLog.log(Log.BRIEF,
 356                     "default port for server socket factory " + ssf +
 357                     " and client socket factory " + csf +
 358                     " set to " + port);
 359             }
 360         }
 361     }
 362 
 363     /**
 364      * Returns transport for making connections to remote endpoints;
 365      * (here, the default transport at port 0 is used).
 366      */
 367     public Transport getOutboundTransport() {
 368         TCPEndpoint localEndpoint = getLocalEndpoint(0, null, null);
 369         return localEndpoint.transport;
 370     }
 371 
 372     /**
 373      * Returns the current list of known transports.
 374      * The returned list is an unshared collection of Transports,
 375      * including all transports which may have channels to remote
 376      * endpoints.
 377      */
 378     private static Collection<TCPTransport> allKnownTransports() {
 379         // Loop through local endpoints, getting the transport of each one.
 380         Set<TCPTransport> s;
 381         synchronized (localEndpoints) {
 382             // presize s to number of localEndpoints
 383             s = new HashSet<TCPTransport>(localEndpoints.size());
 384             for (LinkedList<TCPEndpoint> epList : localEndpoints.values()) {
 385                 /*
 386                  * Each local endpoint has its transport added to s.
 387                  * Note: the transport is the same for all endpoints
 388                  * in the list, so it is okay to pick any one of them.
 389                  */
 390                 TCPEndpoint ep = epList.getFirst();
 391                 s.add(ep.transport);
 392             }
 393         }
 394         return s;
 395     }
 396 
 397     /**
 398      * Release idle outbound connections to reduce demand on I/O resources.
 399      * All transports are asked to release excess connections.
 400      */
 401     public static void shedConnectionCaches() {
 402         for (TCPTransport transport : allKnownTransports()) {
 403             transport.shedConnectionCaches();
 404         }
 405     }
 406 
 407     /**
 408      * Export the object to accept incoming calls.
 409      */
 410     public void exportObject(Target target) throws RemoteException {
 411         transport.exportObject(target);
 412     }
 413 
 414     /**
 415      * Returns a channel for this (remote) endpoint.
 416      */
 417     public Channel getChannel() {
 418         return getOutboundTransport().getChannel(this);
 419     }
 420 
 421     /**
 422      * Returns address for endpoint
 423      */
 424     public String getHost() {
 425         return host;
 426     }
 427 
 428     /**
 429      * Returns the port for this endpoint.  If this endpoint was
 430      * created as a server endpoint (using getLocalEndpoint) for a
 431      * default/anonymous port and its inbound transport has started
 432      * listening, this method returns (instead of zero) the actual
 433      * bound port suitable for passing to clients.
 434      **/
 435     public int getPort() {
 436         return port;
 437     }
 438 
 439     /**
 440      * Returns the port that this endpoint's inbound transport listens
 441      * on, if this endpoint was created as a server endpoint (using
 442      * getLocalEndpoint).  If this endpoint was created for the
 443      * default/anonymous port, then this method returns zero even if
 444      * the transport has started listening.
 445      **/
 446     public int getListenPort() {
 447         return listenPort;
 448     }
 449 
 450     /**
 451      * Returns the transport for incoming connections to this
 452      * endpoint, if this endpoint was created as a server endpoint
 453      * (using getLocalEndpoint).
 454      **/
 455     public Transport getInboundTransport() {
 456         return transport;
 457     }
 458 
 459     /**
 460      * Get the client socket factory associated with this endpoint.
 461      */
 462     public RMIClientSocketFactory getClientSocketFactory() {
 463         return csf;
 464     }
 465 
 466     /**
 467      * Get the server socket factory associated with this endpoint.
 468      */
 469     public RMIServerSocketFactory getServerSocketFactory() {
 470         return ssf;
 471     }
 472 
 473     /**
 474      * Return string representation for endpoint.
 475      */
 476     public String toString() {
 477         return "[" + host + ":" + port +
 478             (ssf != null ? "," + ssf : "") +
 479             (csf != null ? "," + csf : "") +
 480             "]";
 481     }
 482 
 483     public int hashCode() {
 484         return port;
 485     }
 486 
 487     public boolean equals(Object obj) {
 488         if ((obj != null) && (obj instanceof TCPEndpoint)) {
 489             TCPEndpoint ep = (TCPEndpoint) obj;
 490             if (port != ep.port || !host.equals(ep.host))
 491                 return false;
 492             if (((csf == null) ^ (ep.csf == null)) ||
 493                 ((ssf == null) ^ (ep.ssf == null)))
 494                 return false;
 495             /*
 496              * Fix for 4254510: perform socket factory *class* equality check
 497              * before socket factory equality check to avoid passing
 498              * a potentially naughty socket factory to this endpoint's
 499              * {client,server} socket factory equals method.
 500              */
 501             if ((csf != null) &&
 502                 !(csf.getClass() == ep.csf.getClass() && csf.equals(ep.csf)))
 503                 return false;
 504             if ((ssf != null) &&
 505                 !(ssf.getClass() == ep.ssf.getClass() && ssf.equals(ep.ssf)))
 506                 return false;
 507             return true;
 508         } else {
 509             return false;
 510         }
 511     }
 512 
 513     /* codes for the self-describing formats of wire representation */
 514     private static final int FORMAT_HOST_PORT           = 0;
 515     private static final int FORMAT_HOST_PORT_FACTORY   = 1;
 516 
 517     /**
 518      * Write endpoint to output stream.
 519      */
 520     public void write(ObjectOutput out) throws IOException {
 521         if (csf == null) {
 522             out.writeByte(FORMAT_HOST_PORT);
 523             out.writeUTF(host);
 524             out.writeInt(port);
 525         } else {
 526             out.writeByte(FORMAT_HOST_PORT_FACTORY);
 527             out.writeUTF(host);
 528             out.writeInt(port);
 529             out.writeObject(csf);
 530         }
 531     }
 532 
 533     /**
 534      * Get the endpoint from the input stream.
 535      * @param in the input stream
 536      * @exception IOException If id could not be read (due to stream failure)
 537      */
 538     public static TCPEndpoint read(ObjectInput in)
 539         throws IOException, ClassNotFoundException
 540     {
 541         String host;
 542         int port;
 543         RMIClientSocketFactory csf = null;
 544 
 545         byte format = in.readByte();
 546         switch (format) {
 547           case FORMAT_HOST_PORT:
 548             host = in.readUTF();
 549             port = in.readInt();
 550             break;
 551 
 552           case FORMAT_HOST_PORT_FACTORY:
 553             host = in.readUTF();
 554             port = in.readInt();
 555             csf = (RMIClientSocketFactory) in.readObject();
 556           break;
 557 
 558           default:
 559             throw new IOException("invalid endpoint format");
 560         }
 561         return new TCPEndpoint(host, port, csf, null);
 562     }
 563 
 564     /**
 565      * Write endpoint to output stream in older format used by
 566      * UnicastRef for JDK1.1 compatibility.
 567      */
 568     public void writeHostPortFormat(DataOutput out) throws IOException {
 569         if (csf != null) {
 570             throw new InternalError("TCPEndpoint.writeHostPortFormat: " +
 571                 "called for endpoint with non-null socket factory");
 572         }
 573         out.writeUTF(host);
 574         out.writeInt(port);
 575     }
 576 
 577     /**
 578      * Create a new endpoint from input stream data.
 579      * @param in the input stream
 580      */
 581     public static TCPEndpoint readHostPortFormat(DataInput in)
 582         throws IOException
 583     {
 584         String host = in.readUTF();
 585         int port = in.readInt();
 586         return new TCPEndpoint(host, port);
 587     }
 588 
 589     private static RMISocketFactory chooseFactory() {
 590         RMISocketFactory sf = RMISocketFactory.getSocketFactory();
 591         if (sf == null) {
 592             sf = TCPTransport.defaultSocketFactory;
 593         }
 594         return sf;
 595     }
 596 
 597     /**
 598      * Open and return new client socket connection to endpoint.
 599      */
 600     Socket newSocket() throws RemoteException {
 601         if (TCPTransport.tcpLog.isLoggable(Log.VERBOSE)) {
 602             TCPTransport.tcpLog.log(Log.VERBOSE,
 603                 "opening socket to " + this);
 604         }
 605 
 606         Socket socket;
 607 
 608         try {
 609             RMIClientSocketFactory clientFactory = csf;
 610             if (clientFactory == null) {
 611                 clientFactory = chooseFactory();
 612             }
 613             socket = clientFactory.createSocket(host, port);
 614 
 615         } catch (java.net.UnknownHostException e) {
 616             throw new java.rmi.UnknownHostException(
 617                 "Unknown host: " + host, e);
 618         } catch (java.net.ConnectException e) {
 619             throw new java.rmi.ConnectException(
 620                 "Connection refused to host: " + host, e);
 621         } catch (IOException e) {
 622             // We might have simply run out of file descriptors
 623             try {
 624                 TCPEndpoint.shedConnectionCaches();
 625                 // REMIND: should we retry createSocket?
 626             } catch (OutOfMemoryError | Exception mem) {
 627                 // don't quit if out of memory
 628                 // or shed fails non-catastrophically
 629             }
 630 
 631             throw new ConnectIOException("Exception creating connection to: " +
 632                 host, e);
 633         }
 634 
 635         // set socket to disable Nagle's algorithm (always send immediately)
 636         // TBD: should this be left up to socket factory instead?
 637         try {
 638             socket.setTcpNoDelay(true);
 639         } catch (Exception e) {
 640             // if we fail to set this, ignore and proceed anyway
 641         }
 642 
 643         // fix 4187495: explicitly set SO_KEEPALIVE to prevent client hangs
 644         try {
 645             socket.setKeepAlive(true);
 646         } catch (Exception e) {
 647             // ignore and proceed
 648         }
 649 
 650         return socket;
 651     }
 652 
 653     /**
 654      * Return new server socket to listen for connections on this endpoint.
 655      */
 656     ServerSocket newServerSocket() throws IOException {
 657         if (TCPTransport.tcpLog.isLoggable(Log.VERBOSE)) {
 658             TCPTransport.tcpLog.log(Log.VERBOSE,
 659                 "creating server socket on " + this);
 660         }
 661 
 662         RMIServerSocketFactory serverFactory = ssf;
 663         if (serverFactory == null) {
 664             serverFactory = chooseFactory();
 665         }
 666         ServerSocket server = serverFactory.createServerSocket(listenPort);
 667 
 668         // if we listened on an anonymous port, set the default port
 669         // (for this socket factory)
 670         if (listenPort == 0)
 671             setDefaultPort(server.getLocalPort(), csf, ssf);
 672 
 673         return server;
 674     }
 675 
 676     /**
 677      * The class FQDN encapsulates a routine that makes a best effort
 678      * attempt to retrieve the fully qualified domain name of the local
 679      * host.
 680      *
 681      * @author  Laird Dornin
 682      */
 683     private static class FQDN implements Runnable {
 684 
 685         /**
 686          * strings in which we can store discovered fqdn
 687          */
 688         private String reverseLookup;
 689 
 690         private String hostAddress;
 691 
 692         private FQDN(String hostAddress) {
 693             this.hostAddress = hostAddress;
 694         }
 695 
 696         /**
 697          * Do our best to obtain a fully qualified hostname for the local
 698          * host.  Perform the following steps to get a localhostname:
 699          *
 700          * 1. InetAddress.getLocalHost().getHostName() - if contains
 701          *    '.' use as FQDN
 702          * 2. if no '.' query name service for FQDN in a thread
 703          *    Note: We query the name service for an FQDN by creating
 704          *    an InetAddress via a stringified copy of the local ip
 705          *    address; this creates an InetAddress with a null hostname.
 706          *    Asking for the hostname of this InetAddress causes a name
 707          *    service lookup.
 708          *
 709          * 3. if name service takes too long to return, use ip address
 710          * 4. if name service returns but response contains no '.'
 711          *    default to ipaddress.
 712          */
 713         static String attemptFQDN(InetAddress localAddr)
 714             throws java.net.UnknownHostException
 715         {
 716 
 717             String hostName = localAddr.getHostName();
 718 
 719             if (hostName.indexOf('.') < 0 ) {
 720 
 721                 String hostAddress = localAddr.getHostAddress();
 722                 FQDN f = new FQDN(hostAddress);
 723 
 724                 int nameServiceTimeOut =
 725                     TCPEndpoint.getInt("sun.rmi.transport.tcp.localHostNameTimeOut",
 726                                        10000);
 727 
 728                 try {
 729                     synchronized(f) {
 730                         f.getFQDN();
 731 
 732                         /* wait to obtain an FQDN */
 733                         f.wait(nameServiceTimeOut);
 734                     }
 735                 } catch (InterruptedException e) {
 736                     /* propagate the exception to the caller */
 737                     Thread.currentThread().interrupt();
 738                 }
 739                 hostName = f.getHost();
 740 
 741                 if ((hostName == null) || (hostName.equals(""))
 742                     || (hostName.indexOf('.') < 0 )) {
 743 
 744                     hostName = hostAddress;
 745                 }
 746             }
 747             return hostName;
 748         }
 749 
 750         /**
 751          * Method that that will start a thread to wait to retrieve a
 752          * fully qualified domain name from a name service.  The spawned
 753          * thread may never return but we have marked it as a daemon so the vm
 754          * will terminate appropriately.
 755          */
 756         private void getFQDN() {
 757 
 758             /* FQDN finder will run in RMI threadgroup. */
 759             Thread t = AccessController.doPrivileged(
 760                 new NewThreadAction(FQDN.this, "FQDN Finder", true));
 761             t.start();
 762         }
 763 
 764         private synchronized String getHost() {
 765             return reverseLookup;
 766         }
 767 
 768         /**
 769          * thread to query a name service for the fqdn of this host.
 770          */
 771         public void run()  {
 772 
 773             String name = null;
 774 
 775             try {
 776                 name = InetAddress.getByName(hostAddress).getHostName();
 777             } catch (java.net.UnknownHostException e) {
 778             } finally {
 779                 synchronized(this) {
 780                     reverseLookup = name;
 781                     this.notify();
 782                 }
 783             }
 784         }
 785     }
 786 }