1 /*
   2  * Copyright (c) 2000, 2018, 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 java.net;
  26 
  27 import java.io.IOException;
  28 import java.io.InvalidObjectException;
  29 import java.io.ObjectInputStream;
  30 import java.io.ObjectOutputStream;
  31 import java.io.ObjectStreamException;
  32 import java.io.ObjectStreamField;
  33 
  34 /**
  35  *
  36  * This class implements an IP Socket Address (IP address + port number)
  37  * It can also be a pair (hostname + port number), in which case an attempt
  38  * will be made to resolve the hostname. If resolution fails then the address
  39  * is said to be <I>unresolved</I> but can still be used on some circumstances
  40  * like connecting through a proxy.
  41  * <p>
  42  * It provides an immutable object used by sockets for binding, connecting, or
  43  * as returned values.
  44  * <p>
  45  * The <i>wildcard</i> is a special local IP address. It usually means "any"
  46  * and can only be used for {@code bind} operations.
  47  *
  48  * @see java.net.Socket
  49  * @see java.net.ServerSocket
  50  * @since 1.4
  51  */
  52 public class InetSocketAddress
  53     extends SocketAddress
  54 {
  55     // Private implementation class pointed to by all public methods.
  56     private static class InetSocketAddressHolder {
  57         // The hostname of the Socket Address
  58         private String hostname;
  59         // The IP address of the Socket Address
  60         private InetAddress addr;
  61         // The port number of the Socket Address
  62         private int port;
  63 
  64         private InetSocketAddressHolder(String hostname, InetAddress addr, int port) {
  65             this.hostname = hostname;
  66             this.addr = addr;
  67             this.port = port;
  68         }
  69 
  70         private int getPort() {
  71             return port;
  72         }
  73 
  74         private InetAddress getAddress() {
  75             return addr;
  76         }
  77 
  78         private String getHostName() {
  79             if (hostname != null)
  80                 return hostname;
  81             if (addr != null)
  82                 return addr.getHostName();
  83             return null;
  84         }
  85 
  86         private String getHostString() {
  87             if (hostname != null)
  88                 return hostname;
  89             if (addr != null) {
  90                 if (addr.holder().getHostName() != null)
  91                     return addr.holder().getHostName();
  92                 else
  93                     return addr.getHostAddress();
  94             }
  95             return null;
  96         }
  97 
  98         private boolean isUnresolved() {
  99             return addr == null;
 100         }
 101 
 102         @Override
 103         public String toString() {
 104             if (isUnresolved()) {
 105                 return hostname + ":" + port;
 106             } else {
 107                 return addr.toString() + ":" + port;
 108             }
 109         }
 110 
 111         @Override
 112         public final boolean equals(Object obj) {
 113             if (obj == null || !(obj instanceof InetSocketAddressHolder))
 114                 return false;
 115             InetSocketAddressHolder that = (InetSocketAddressHolder)obj;
 116             boolean sameIP;
 117             if (addr != null)
 118                 sameIP = addr.equals(that.addr);
 119             else if (hostname != null)
 120                 sameIP = (that.addr == null) &&
 121                     hostname.equalsIgnoreCase(that.hostname);
 122             else
 123                 sameIP = (that.addr == null) && (that.hostname == null);
 124             return sameIP && (port == that.port);
 125         }
 126 
 127         @Override
 128         public final int hashCode() {
 129             if (addr != null)
 130                 return addr.hashCode() + port;
 131             if (hostname != null)
 132                 return hostname.toLowerCase().hashCode() + port;
 133             return port;
 134         }
 135     }
 136 
 137     private final transient InetSocketAddressHolder holder;
 138 
 139     @java.io.Serial
 140     private static final long serialVersionUID = 5076001401234631237L;
 141 
 142     private static int checkPort(int port) {
 143         if (port < 0 || port > 0xFFFF)
 144             throw new IllegalArgumentException("port out of range:" + port);
 145         return port;
 146     }
 147 
 148     private static String checkHost(String hostname) {
 149         if (hostname == null)
 150             throw new IllegalArgumentException("hostname can't be null");
 151         return hostname;
 152     }
 153 
 154     /**
 155      * Creates a socket address where the IP address is the wildcard address
 156      * and the port number a specified value.
 157      * <p>
 158      * A valid port value is between 0 and 65535.
 159      * A port number of {@code zero} will let the system pick up an
 160      * ephemeral port in a {@code bind} operation.
 161      *
 162      * @param   port    The port number
 163      * @throws IllegalArgumentException if the port parameter is outside the specified
 164      * range of valid port values.
 165      */
 166     public InetSocketAddress(int port) {
 167         this(InetAddress.anyLocalAddress(), port);
 168     }
 169 
 170     /**
 171      *
 172      * Creates a socket address from an IP address and a port number.
 173      * <p>
 174      * A valid port value is between 0 and 65535.
 175      * A port number of {@code zero} will let the system pick up an
 176      * ephemeral port in a {@code bind} operation.
 177      * <P>
 178      * A {@code null} address will assign the <i>wildcard</i> address.
 179      *
 180      * @param   addr    The IP address
 181      * @param   port    The port number
 182      * @throws IllegalArgumentException if the port parameter is outside the specified
 183      * range of valid port values.
 184      */
 185     public InetSocketAddress(InetAddress addr, int port) {
 186         holder = new InetSocketAddressHolder(
 187                         null,
 188                         addr == null ? InetAddress.anyLocalAddress() : addr,
 189                         checkPort(port));
 190     }
 191 
 192     /**
 193      *
 194      * Creates a socket address from a hostname and a port number.
 195      * <p>
 196      * An attempt will be made to resolve the hostname into an InetAddress.
 197      * If that attempt fails, the address will be flagged as <I>unresolved</I>.
 198      * <p>
 199      * If there is a security manager, its {@code checkConnect} method
 200      * is called with the host name as its argument to check the permission
 201      * to resolve it. This could result in a SecurityException.
 202      * <P>
 203      * A valid port value is between 0 and 65535.
 204      * A port number of {@code zero} will let the system pick up an
 205      * ephemeral port in a {@code bind} operation.
 206      *
 207      * @param   hostname the Host name
 208      * @param   port    The port number
 209      * @throws IllegalArgumentException if the port parameter is outside the range
 210      * of valid port values, or if the hostname parameter is {@code null}.
 211      * @throws SecurityException if a security manager is present and
 212      *                           permission to resolve the host name is
 213      *                           denied.
 214      * @see     #isUnresolved()
 215      */
 216     public InetSocketAddress(String hostname, int port) {
 217         checkHost(hostname);
 218         InetAddress addr = null;
 219         String host = null;
 220         try {
 221             addr = InetAddress.getByName(hostname);
 222         } catch(UnknownHostException e) {
 223             host = hostname;
 224         }
 225         holder = new InetSocketAddressHolder(host, addr, checkPort(port));
 226     }
 227 
 228     // private constructor for creating unresolved instances
 229     private InetSocketAddress(int port, String hostname) {
 230         holder = new InetSocketAddressHolder(hostname, null, port);
 231     }
 232 
 233     /**
 234      *
 235      * Creates an unresolved socket address from a hostname and a port number.
 236      * <p>
 237      * No attempt will be made to resolve the hostname into an InetAddress.
 238      * The address will be flagged as <I>unresolved</I>.
 239      * <p>
 240      * A valid port value is between 0 and 65535.
 241      * A port number of {@code zero} will let the system pick up an
 242      * ephemeral port in a {@code bind} operation.
 243      *
 244      * @param   host    the Host name
 245      * @param   port    The port number
 246      * @throws IllegalArgumentException if the port parameter is outside
 247      *                  the range of valid port values, or if the hostname
 248      *                  parameter is {@code null}.
 249      * @see     #isUnresolved()
 250      * @return  an {@code InetSocketAddress} representing the unresolved
 251      *          socket address
 252      * @since 1.5
 253      */
 254     public static InetSocketAddress createUnresolved(String host, int port) {
 255         return new InetSocketAddress(checkPort(port), checkHost(host));
 256     }
 257 
 258     /**
 259      * @serialField hostname String
 260      * @serialField addr InetAddress
 261      * @serialField port int
 262      */
 263     @java.io.Serial
 264     private static final ObjectStreamField[] serialPersistentFields = {
 265          new ObjectStreamField("hostname", String.class),
 266          new ObjectStreamField("addr", InetAddress.class),
 267          new ObjectStreamField("port", int.class)};
 268 
 269     @java.io.Serial
 270     private void writeObject(ObjectOutputStream out)
 271         throws IOException
 272     {
 273         // Don't call defaultWriteObject()
 274          ObjectOutputStream.PutField pfields = out.putFields();
 275          pfields.put("hostname", holder.hostname);
 276          pfields.put("addr", holder.addr);
 277          pfields.put("port", holder.port);
 278          out.writeFields();
 279      }
 280 
 281     @java.io.Serial
 282     private void readObject(ObjectInputStream in)
 283         throws IOException, ClassNotFoundException
 284     {
 285         // Don't call defaultReadObject()
 286         ObjectInputStream.GetField oisFields = in.readFields();
 287         final String oisHostname = (String)oisFields.get("hostname", null);
 288         final InetAddress oisAddr = (InetAddress)oisFields.get("addr", null);
 289         final int oisPort = oisFields.get("port", -1);
 290 
 291         // Check that our invariants are satisfied
 292         checkPort(oisPort);
 293         if (oisHostname == null && oisAddr == null)
 294             throw new InvalidObjectException("hostname and addr " +
 295                                              "can't both be null");
 296 
 297         InetSocketAddressHolder h = new InetSocketAddressHolder(oisHostname,
 298                                                                 oisAddr,
 299                                                                 oisPort);
 300         UNSAFE.putReference(this, FIELDS_OFFSET, h);
 301     }
 302 
 303     @java.io.Serial
 304     private void readObjectNoData()
 305         throws ObjectStreamException
 306     {
 307         throw new InvalidObjectException("Stream data required");
 308     }
 309 
 310     private static final jdk.internal.misc.Unsafe UNSAFE
 311             = jdk.internal.misc.Unsafe.getUnsafe();
 312     private static final long FIELDS_OFFSET
 313             = UNSAFE.objectFieldOffset(InetSocketAddress.class, "holder");
 314 
 315     /**
 316      * Gets the port number.
 317      *
 318      * @return the port number.
 319      */
 320     public final int getPort() {
 321         return holder.getPort();
 322     }
 323 
 324     /**
 325      * Gets the {@code InetAddress}.
 326      *
 327      * @return the InetAddress or {@code null} if it is unresolved.
 328      */
 329     public final InetAddress getAddress() {
 330         return holder.getAddress();
 331     }
 332 
 333     /**
 334      * Gets the {@code hostname}.
 335      * Note: This method may trigger a name service reverse lookup if the
 336      * address was created with a literal IP address.
 337      *
 338      * @return  the hostname part of the address.
 339      */
 340     public final String getHostName() {
 341         return holder.getHostName();
 342     }
 343 
 344     /**
 345      * Returns the hostname, or the String form of the address if it
 346      * doesn't have a hostname (it was created using a literal).
 347      * This has the benefit of <b>not</b> attempting a reverse lookup.
 348      *
 349      * @return the hostname, or String representation of the address.
 350      * @since 1.7
 351      */
 352     public final String getHostString() {
 353         return holder.getHostString();
 354     }
 355 
 356     /**
 357      * Checks whether the address has been resolved or not.
 358      *
 359      * @return {@code true} if the hostname couldn't be resolved into
 360      *          an {@code InetAddress}.
 361      */
 362     public final boolean isUnresolved() {
 363         return holder.isUnresolved();
 364     }
 365 
 366     /**
 367      * Constructs a string representation of this InetSocketAddress.
 368      * This String is constructed by calling toString() on the InetAddress
 369      * and concatenating the port number (with a colon). If the address
 370      * is unresolved then the part before the colon will only contain the hostname.
 371      *
 372      * @return  a string representation of this object.
 373      */
 374     @Override
 375     public String toString() {
 376         return holder.toString();
 377     }
 378 
 379     /**
 380      * Compares this object against the specified object.
 381      * The result is {@code true} if and only if the argument is
 382      * not {@code null} and it represents the same address as
 383      * this object.
 384      * <p>
 385      * Two instances of {@code InetSocketAddress} represent the same
 386      * address if both the InetAddresses (or hostnames if it is unresolved) and port
 387      * numbers are equal.
 388      * If both addresses are unresolved, then the hostname and the port number
 389      * are compared.
 390      *
 391      * Note: Hostnames are case insensitive. e.g. "FooBar" and "foobar" are
 392      * considered equal.
 393      *
 394      * @param   obj   the object to compare against.
 395      * @return  {@code true} if the objects are the same;
 396      *          {@code false} otherwise.
 397      * @see java.net.InetAddress#equals(java.lang.Object)
 398      */
 399     @Override
 400     public final boolean equals(Object obj) {
 401         if (obj == null || !(obj instanceof InetSocketAddress))
 402             return false;
 403         return holder.equals(((InetSocketAddress) obj).holder);
 404     }
 405 
 406     /**
 407      * Returns a hashcode for this socket address.
 408      *
 409      * @return  a hash code value for this socket address.
 410      */
 411     @Override
 412     public final int hashCode() {
 413         return holder.hashCode();
 414     }
 415 }